remotes::install_github("jtextor/dagitty/r")
Using GitHub PAT from the git credential store.
Skipping install of 'dagitty' from a github remote, the SHA1 (7a657776) has not changed since last install.
Use `force = TRUE` to force installation
library(dagitty)
library(ggdag)
Attaching package: ‘ggdag’
The following object is masked from ‘package:stats’:
filter
library(tidyverse)
── Attaching core tidyverse packages ───────────────────────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.2 ✔ tibble 3.2.1
✔ lubridate 1.9.4 ✔ tidyr 1.3.1
✔ purrr 1.0.4 ── Conflicts ─────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks ggdag::filter(), stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
The goal of this notebook is to explain how to estimate the average
treatment effect of a dichotomous (binary) treatment on change from a
continuous baseline measure (Tennant et al.
2021) under three different scenarios:
- A Randomized Controlled Trial (RCT) with no loss to follow-up.
- An RCT with loss to follow-up.
- An observational study with treatment confounders and loss to
follow-up.
Each approach uses the same core data generating model with a
proportional treatment effect, where the treatment works better in
patients with lower baseline scores.
Randomized Controlled Trial With No Loss to Follow-Up
In this simulation:
- severity is a latent (unobserved) variable that
affects:
Baseline scores (sicker patients have lower scores)
Treatment effect (treatment works better for sicker
patients)
Note that in this model severity is not affected by treatment or
time. It is set at baseline for each patient and remains the same for
each time period.
The Causal DAG for this data generating model is shown below.
# Create the DAG using dagify
dagRCT <- dagify(
baseline ~ severity,
time1 ~ baseline + group + severity,
change1 ~ baseline + time1,
time2 ~ group + time1 + severity,
change2 ~ baseline + time2,
exposure = "group",
outcome = "time2",
coords = data.frame(
name = c("baseline", "severity", "group", "time1", "change1", "time2", "change2"),
x = c(0, 0, 4, 3, 1.5, 5.5, 4),
y = c(1, 2, 2, 1, 0, 1, 0)
)
)
# Change status of severity to indicate that it is a latent variable
latents(dagRCT) = "severity"
# Convert the dagitty graph to a ggdag group for prettier printing
tidy_dagRCT <- tidy_dagitty(dagRCT)
# Plot with custom node shapes
# Get the status (exposure, outcome) information and set variables without a status to "observed"
status_data <- ggdag_status(tidy_dagRCT)$data %>% mutate(status = ifelse(is.na(status), "observed", as.character(status)))
#status_data <- status_data %>% mutate(gstatus = as.character(status), gstatus = ifelse(name == "censored", "adjusted", as.character(status)),
# gstatus = factor(gstatus,levels = c(levels(status), "adjusted")))
# Plot with custom node shapes while preserving status coloring
ggplot(status_data, aes(x = x, y = y, xend = xend, yend = yend)) +
geom_dag_edges(arrow_directed = grid::arrow(length = unit(0.5, "cm"), type = "closed")) +
geom_dag_point(aes(color = status), size = 24) +
geom_dag_text(color = "white") +
scale_shape_manual(values = c(circle = 16, square = 15), guide = "none") +
scale_color_discrete(name = "Status", na.value = "grey50") +
theme_dag() +
theme(legend.position = "right")

NA
NA
Now let’s simulate data for the RCT. This simulates and retains the
potential outcomes Y0_time1, Y1_time1, Y0_time2 and Y1_time2 for all
patients, then sets time1 and time2 for a patient based on that
patient’s assigned treatment in the data. This means that we can
calculate the true causal effect from the potential outcomes.
# Set seed for reproducibility
set.seed(124)
# Simulate data for demonstration
# 150 participants, 2 groups (treatment and control)
n <- 150
group <- rep(c(0, 1), each = n/2) # 0 = control, 1 = treatment
# Generate baseline scores (similar for both groups)
baseline <- rnorm(n, mean = 50, sd = 10)
# Add a latent "severity" variable that affects baseline score
severity <- rnorm(n, mean = 0, sd = 1)
# Adjust baseline by severity (sicker patients have lower baseline scores)
baseline <- baseline - 5 * severity
# Generate follow-up scores with proportional effects
prop_effect1 <- 0.20 # 20% improvement at time1 for treatment group
prop_effect2 <- 0.15 # Additional 15% improvement at time2 for treatment group
# Calculate potential outcomes
# True effect is proportional to baseline
# The treatment also has a stronger effect for patients with higher severity
Y0_time1 <- baseline + rnorm(n, mean = 0, sd = 3)
Y1_time1 <- baseline * (1 + prop_effect1 + 0.1 * severity) + rnorm(n, mean = 0, sd = 3)
Y0_time2 <- Y0_time1 + rnorm(n, mean = 0, sd = 3)
Y1_time2 <- Y1_time1 * (1 + prop_effect1 + 0.1 * severity) + rnorm(n, mean = 0, sd = 3)
# Calculate time1 values - true effect is proportional to baseline
# The treatment also has a stronger effect for patients with higher severity
# time1 <- vector("numeric", n)
# for (i in 1:n) {
# if (group[i] == 1) {
# # Treatment group: effect is proportional to baseline and increased by severity
# effect_modifier <- 1 + prop_effect1 + 0.1 * severity[i]
# time1[i] <- baseline[i] * effect_modifier + rnorm(1, mean = 0, sd = 3)
# } else {
# # Control group: small random change
# time1[i] <- baseline[i] + rnorm(1, mean = 0, sd = 3)
# }
# }
# time2 <- vector("numeric", n)
# for (i in 1:n) {
# if (group[i] == 1) {
# # Treatment group: additional effect proportional to time1
# effect_modifier <- 1 + prop_effect2 + 0.05 * severity[i]
# time2[i] <- time1[i] * effect_modifier + rnorm(1, mean = 0, sd = 3)
# } else {
# # Control group: small random change
# time2[i] <- time1[i] + rnorm(1, mean = 0, sd = 3)
# }
# }
# Set the observed time1 and time2 based on treatment (group) assignment
time1 <- ifelse(group == 0, Y0_time1, Y1_time1)
time2 <- ifelse(group == 0, Y0_time2, Y1_time2)
# Create data frame
data <- data.frame(
id = 1:n,
group = factor(group, labels = c("Control", "Treatment")),
baseline = baseline,
time1 = time1,
time2 = time2,
Y0_time1 = Y0_time1,
Y1_time1 = Y1_time1,
Y0_time2 = Y0_time2,
Y1_time2 = Y1_time2,
severity = severity # Include the latent severity variable
)
# Create change scores for the full dataset
data$change1 <- data$time1 - data$baseline
data$change2 <- data$time2 - data$baseline
data
For publication ready tables, we can use gtsummary. Here is the
cannonical Table 1:
library(gtsummary)
table1 <- data |> tbl_summary()
table1
| Characteristic |
N = 150 |
| id |
76 (38, 113) |
| group |
|
| Control |
75 (50%) |
| Treatment |
75 (50%) |
| baseline |
50 (43, 57) |
| time1 |
55 (46, 64) |
| time2 |
60 (48, 71) |
| Y0_time1 |
50 (43, 57) |
| Y1_time1 |
58 (52, 68) |
| Y0_time2 |
50 (43, 57) |
| Y1_time2 |
69 (60, 79) |
| severity |
-0.08 (-0.66, 0.51) |
| change1 |
3 (0, 10) |
| change2 |
6 (-1, 19) |
And here is the Canonical Table 2. This shows a significant
difference between time2 for the treatment and control
groups.
table2 <- tbl_summary(data, by = group) |> add_p() |> bold_labels()
table2
| Characteristic |
Control
N = 75 |
Treatment
N = 75 |
p-value |
| id |
38 (19, 57) |
113 (94, 132) |
<0.001 |
| baseline |
50 (42, 56) |
50 (44, 58) |
0.6 |
| time1 |
51 (43, 57) |
61 (52, 68) |
<0.001 |
| time2 |
51 (41, 57) |
71 (61, 85) |
<0.001 |
| Y0_time1 |
51 (43, 57) |
50 (43, 57) |
0.9 |
| Y1_time1 |
57 (51, 66) |
61 (52, 68) |
0.2 |
| Y0_time2 |
51 (41, 57) |
49 (43, 58) |
0.8 |
| Y1_time2 |
66 (59, 77) |
71 (61, 85) |
0.14 |
| severity |
-0.23 (-0.69, 0.51) |
0.02 (-0.61, 0.58) |
0.3 |
| change1 |
0 (-3, 2) |
10 (6, 14) |
<0.001 |
| change2 |
-1 (-3, 3) |
19 (13, 30) |
<0.001 |
With all the potential outcomes we can calculate the Average
Treatment Effect of the treatment on time2 as:
mean(data$Y1_time2) - mean(data$Y0_time2)
[1] 20.22007
This is just the mean of the raw individual treatment effects:
mean(data$Y1_time2 - data$Y0_time2)
[1] 20.22007
This value is not adjusted for the baseline value of each
patient.
We can also compute the difference in change scores between the
treated and untreated, which is the causal estimand showing the effect
of treatment, \(X\), on change in score
\(Y\), where \(Y_1^x\) is the potential outcome of \(Y_1\) the follow-up score when \(X\) is set to \(x\) and \(Y_1^{x'}\) is the potential outcome of
…
\[
\mathrm{E}(Y_1^x - Y_0^x) - \mathrm{E}(Y_1^{x'} - Y_0^{x'})
\] {#eq-changescore}
When we have both potential outcomes for the populations, this is
identical to the equations above, because the baselines cancel out
across the two means below. In (eq-changescore?)
mean(data$Y1_time2 - data$baseline) - mean(data$Y0_time2 - data$baseline)
[1] 20.22007
While there is only one measured covariate here
(baseline) a covariate balance plot showing the
standardized mean differences between covariates in the treated and
untreated groups (treatment mean - control mean) will show whether there
is covariate imbalance due to the small sample size. Here, we include
the latent variable severity. Even though we have an RCT,
both covariates are skewed to the right.
library(cobalt)
unadjcov <- bal.tab(group ~ baseline + severity, data = data, thresholds = c(m = .1))
Note: `s.d.denom` not specified; assuming "pooled".
love.plot(unadjcov, stars = "std", thresholds = c(m = .1))

There is some controversy over how to measure total effect of a
treatment on change from baseline. If the baseline values of the
treatment and control group are balanced, then an unbiased average
treatment effect is just the mean of time2 for those in the
treatment group minus the mean for those in the control group. In an RCT
with sufficient sample size, the baselines are expected to be balanced.
Let’s check our data:
data %>% group_by(group) %>% summarize(meanBaseline = mean(baseline))
With the relatively small sample size, the baseline means are close,
but not perfectly balanced. This could bias our effect estimate if we
simply use the difference in means of time2 between
Treatment and Control. The means of time2 for the Control
and Treatment groups are:
means <- data %>% group_by(group) %>% summarize(mean = mean(time2))
means
Subtracting these to get the average treatment effect is:
filter(means, group=="Treatment")$mean - filter(means, group=="Control")$mean
[1] 22.05297
We can also do a t-test to test for difference in means for
time2 between the treatment and control groups:
t.test(time2 ~ group, data = data)
Welch Two Sample t-test
data: time2 by group
t = -9.2943, df = 126.84, p-value = 5.426e-16
alternative hypothesis: true difference in means between group Control and group Treatment is not equal to 0
95 percent confidence interval:
-26.74824 -17.35770
sample estimates:
mean in group Control mean in group Treatment
49.80446 71.85743
Here, we see that the means are statistically significantly
different.
If the baseline values are unbalanced, the effect estimate of the
difference in means of time2 will be biased. This is why
many analysts recommend always using analysis of covariance (ANCOVA)
where the outcome is regressed on the treatment and adjusted for the
baseline value:
res <- lm(time2 ~ group + baseline, data = data)
summary(res)
Call:
lm(formula = time2 ~ group + baseline, data = data)
Residuals:
Min 1Q Median 3Q Max
-27.399 -5.672 -0.532 5.751 41.044
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -4.8355 4.1091 -1.177 0.241
groupTreatment 21.4722 1.5712 13.666 <2e-16 ***
baseline 1.0938 0.0792 13.811 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 9.618 on 147 degrees of freedom
Multiple R-squared: 0.7252, Adjusted R-squared: 0.7214
F-statistic: 193.9 on 2 and 147 DF, p-value: < 2.2e-16
Here we see that the causal effect of group on
time2 when adjusted for baseline is
coef(res)["group"]. Note above that the baseline means
between treatment and control differ by around .5, which explains the
difference between the effect as computed on the difference in the mean
of time2 between groups, vs. the regression results.
We can use gtsummary for a nicer view of the results:
tbl_regression(res, estimate_fun = purrr::partial(style_ratio, digits = 2))
| Characteristic |
Beta |
95% CI |
p-value |
| group |
|
|
|
| Control |
— |
— |
|
| Treatment |
21.5 |
18.4, 24.6 |
<0.001 |
| baseline |
1.09 |
0.94, 1.25 |
<0.001 |
| Abbreviation: CI = Confidence Interval |
Interpretation: After adjusting for baseline differences, mean
time2 increased by `res$ units for those receiving the
treatment compared with those who did not receive the treatment. We can
see this here:
# Load required libraries
library(ggplot2)
library(plotly)
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
mean_baseline = mean(data$baseline)
# Calculate predictions at mean baseline for each group
pred1 <- predict(res, newdata = data.frame(group = levels(data$group)[1], baseline = mean_baseline))
pred2 <- predict(res, newdata = data.frame(group = levels(data$group)[2], baseline = mean_baseline))
# Create the plot
p <- ggplot(data, aes(x = baseline, y = time2, color = group)) +
# Add points
geom_point(alpha = 0.6, size = 2) +
# Add regression lines for each group
geom_smooth(method = "lm", se = TRUE, alpha = 0.2) +
# Add vertical line at mean baseline
geom_vline(xintercept = mean_baseline, linetype = "dashed", color = "black", size = 0.8) +
# Add points for the endpoints of the vertical line (these will be invisible)
geom_point(aes(x = mean_baseline, y = pred1,
text = paste("Group:", levels(data$group)[1],
"<br>Baseline:", round(mean_baseline, 2),
"<br>Predicted Time2:", round(pred1, 2))),
color = "red", size = 3, alpha = 0.8, inherit.aes = FALSE) +
geom_point(aes(x = mean_baseline, y = pred2,
text = paste("Group:", levels(data$group)[2],
"<br>Baseline:", round(mean_baseline, 2),
"<br>Predicted Time2:", round(pred2, 2))),
color = "red", size = 3, alpha = 0.8, inherit.aes = FALSE) +
# Add the vertical line showing difference between groups at mean baseline
geom_segment(
x = mean_baseline,
xend = mean_baseline,
y = pred1,
yend = pred2,
color = "red",
size = 2,
arrow = arrow(ends = "both", angle = 90, length = unit(0.1, "inches"))
) +
# Customize the plot
labs(
title = "Time2 vs Baseline by Group",
subtitle = paste("Vertical line shows difference between groups at mean baseline (",
round(mean_baseline, 2), ")", sep = ""),
x = "Baseline",
y = "Time2",
color = "Group"
) +
# Clean theme
theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold"),
plot.subtitle = element_text(size = 11, color = "gray40"),
legend.position = "bottom"
)
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.Warning: Ignoring unknown aesthetics: textWarning: Ignoring unknown aesthetics: text
# Convert to plotly for interactive tooltips
ggplotly(p, tooltip = "text") %>%
layout(title = list(text = "Time2 vs Baseline by Group<br><sub>Vertical line shows difference between groups at mean baseline</sub>"))
Warning: Use of `data$group` is discouraged.
ℹ Use `group` instead.Warning: All aesthetics have length 1, but the data has 150 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.Warning: Use of `data$group` is discouraged.
ℹ Use `group` instead.Warning: All aesthetics have length 1, but the data has 150 rows.
ℹ Please consider using `annotate()` or provide this layer with data containing a single row.`geom_smooth()` using formula = 'y ~ x'
Recall that the treatment effect in this model is proportional to the
baseline value and increased by severity, a latent
variable. The average treatment effect reported in the analysis above is
the average across all baseline values in the model.
Simulate proportional treatment effect on change from baseline in an
RCT with censoring
For data with censoring and proportional treatment effects:
This simulation is identical to the one above, but with some subjects
dropping out of the study prior to time2 being measured. Changes noted
in bold.
- severity is latent variable that affects:
Baseline scores (sicker patients have lower scores)
Treatment effect (treatment works better for sicker
patients)
Censoring probability (sicker patients more likely to
drop out)
- Treatment group participants are less likely to be
censored
- Participants with less improvement are more likely to be
censored
The Causal DAG for this data generating model is shown below.
library(dagitty)
library(ggdag)
library(tidyverse)
# Create the DAG using dagify
dag1 <- dagify(
baseline ~ severity,
time1 ~ baseline + group + severity,
change1 ~ baseline + time1,
censored ~ change1 + group + severity,
time2 ~ group + time1 + severity,
exposure = "group",
outcome = "time2",
coords = data.frame(
name = c("baseline", "severity", "group", "time1", "change1", "censored", "time2"),
x = c(0, 0, 4, 3, 1.5, 8, 5.5),
y = c(1, 2, 2, 1, 0, 1, 1)
)
)
# Indicate that `censored` is adjusted since we only have data for patients who are not censored, censored is forced to equal 0
adjustedNodes(dag1) <- "censored"
plot(dag1)
#List all paths from treatment `group` to outcome `time2`
# `paths` currently ignores the adjusted nodes when determining which paths are open, so we have to specify adjusted nodes in the call
paths(dag1, Z = adjustedNodes(dag1))
# Print the adjustment sets (if any)
adjustmentSets(dag1)
tidy_dag1 <- tidy_dagitty(dag1) %>% mutate(shape = ifelse(name == "censored", "square", "circle"))
# Indicate that `censored` is adjusted for in this model
tidy_dag1 <- adjust_for(tidy_dag1, "censored")
# Plot with custom node shapes
# Get the status (exposure, outcome) information
status_data <- ggdag_status(tidy_dag1)$data
status_data <- status_data %>% mutate(gstatus = as.character(status), gstatus = ifelse(name == "censored", "adjusted", as.character(status)),
gstatus = factor(gstatus,levels = c(levels(status), "adjusted")))
# Plot with custom node shapes while preserving status coloring
ggplot(status_data, aes(x = x, y = y, xend = xend, yend = yend)) +
geom_dag_edges(arrow_directed = grid::arrow(length = unit(0.5, "cm"), type = "closed")) +
geom_dag_point(aes(color = gstatus, shape = shape), size = 24) +
geom_dag_text(color = "white") +
scale_shape_manual(values = c(circle = 16, square = 15), guide = "none") +
scale_color_discrete(name = "Status", na.value = "grey50") +
theme_dag() +
theme(legend.position = "right")
The censored variable is shown with a square around it
to indicate that by analyzing only uncensored patients,
censored is adjusted for in the analysis. Since all arrows
point into censored, any path from treatment
group to effect time2 that includes
censored will transmit spurious association through
censored unless the path is blocked at another point.
To see this, lets look at all paths (causal and non-causal) from
group to time2. A causal path is one in which
arrows point from the treatment group to the outcome
time2. Noncausal paths are those with at least one arrow
pointing back toward the treatment.
# `paths` currently ignores the adjusted nodes when determining which paths are open, so we have to specify the adjusted nodes in the call
paths(dag1, Z = adjustedNodes(dag1))
We see that there are 18 paths from group to
time2. $open is a boolean list showing whether each path is
open or closed. All of these paths are open and since many of these
paths are noncausal “backdoor” paths, meaning the arrows do not all
point from group to time2 this means that if
we measure the association between group and
time2 in the data, that association is likely to exhibit
bias from confounding. For example, paths 1 through 11 are all backdoor
paths that go through censored. A collider is a variable on
a path in which both arrows point toward it. As you can see,
censored is a collider on every path it appears in.
Colliders block association when they are not adjusted, but allow
association to flow and bias the analysis when they are adjusted.
To properly measure the total causal effect of group on
time2 we need to measure the association that flows through
all paths that point from group to time2.
Let’s use paths to list only the causal paths:
paths(dag1, Z = adjustedNodes(dag1), directed = TRUE)
There are two causal paths, both open. To measure the total causal
effect, we need to block the non-causal open paths by adjusting for
variables, if possible. We can use ggdag to list the
adjustment sets for the total causal effect of group on
time2. This returns nothing because there is no way to
adjust variables to block the open path caused through the collider
censored.
adjustmentSets(dag1, effect = "total")
We will use IP Weighting for censoring below, but before that, lets
analyze this dataset as if there were no loss to follow-up.
Same model with no loss to follow-up
The same data generating model above includes time2_all with outcome
values for all patients. Without Censoring the Causal DAG for time2_all
looks like this:
library(dagitty)
library(ggdag)
library(tidyverse)
# Create the DAG using dagitify
dag_noloss <- dagify(
baseline ~ severity,
time1 ~ baseline + group + severity,
change1 ~ baseline + time1,
time2_all ~ group + time1 + severity,
exposure = "group",
outcome = "time2_all",
coords = data.frame(
name = c("baseline", "severity", "group", "time1", "change1", "censored", "time2_all"),
x = c(0, 0, 4, 3, 1.5, 8, 5.5),
y = c(1, 2, 2, 1, 0, 1, 1)
)
)
tidy_dag_noloss <- tidy_dagitty(dag_noloss)
# Plot with custom node shapes
# Get the status (exposure, outcome) information
status_data <- ggdag_status(tidy_dag_noloss)$data
status_data <- status_data %>% mutate(gstatus = as.character(status), gstatus = ifelse(name == "censored", "adjusted", as.character(status)),
gstatus = factor(gstatus,levels = c(levels(status), "adjusted")))
# Plot with custom node shapes while preserving status coloring
ggplot(status_data, aes(x = x, y = y, xend = xend, yend = yend)) +
geom_dag_edges(arrow_directed = grid::arrow(length = unit(0.5, "cm"), type = "closed")) +
geom_dag_point(aes(color = gstatus), size = 24) +
geom_dag_text(color = "white") +
scale_shape_manual(values = c(circle = 16, square = 15), guide = "none") +
scale_color_discrete(name = "Status", na.value = "grey50") +
theme_dag() +
theme(legend.position = "right")
# Load required libraries
library(dagitty)
library(ggdag)
library(dplyr)
library(ggplot2)
# Define the DAG
dag1 <- dagitty('dag {
bb="0,0,1,1"
baseline [pos="0.348,0.542"]
censored [adjusted,pos="0.931,0.499"]
change1 [pos="0.477,0.641"]
group [exposure,pos="0.343,0.368"]
severity [pos="0.348,0.449"]
time1 [pos="0.486,0.540"]
time2 [outcome,pos="0.605,0.539"]
baseline -> change1
baseline -> time1
change1 -> censored
group -> censored
group -> time1
group -> time2
severity -> baseline
severity -> censored
severity -> time1
severity -> time2
time1 -> change1
time1 -> time2
}'
)
plot(dag1)
Generate Data According to the Two Causal DAGs Above
# Install and load required packages
# Uncomment to install packages if needed
# install.packages(c("lavaan", "semPlot", "tidyverse", "psych"))
library(lavaan)
library(semPlot)
library(tidyverse)
library(psych)
# Set seed for reproducibility
set.seed(123)
# Simulate data for demonstration
# 150 participants, 2 groups (treatment and control)
n <- 150
group <- rep(c(0, 1), each = n/2) # 0 = control, 1 = treatment
# Generate baseline scores (similar for both groups)
baseline <- rnorm(n, mean = 50, sd = 10)
# Add a latent "severity" variable that affects both baseline and censoring
# This creates the missing not at random (MNAR) scenario
severity <- rnorm(n, mean = 0, sd = 1)
# Adjust baseline by severity (sicker patients have lower baseline scores)
baseline <- baseline - 5 * severity
# Generate follow-up scores with proportional effects
prop_effect1 <- 0.20 # 20% improvement at time1 for treatment group
prop_effect2 <- 0.15 # Additional 15% improvement at time2 for treatment group
# Calculate time1 values - true effect is proportional to baseline
# The treatment also has a stronger effect for patients with higher severity
time1 <- vector("numeric", n)
for (i in 1:n) {
if (group[i] == 1) {
# Treatment group: effect is proportional to baseline and increased by severity
effect_modifier <- 1 + prop_effect1 + 0.1 * severity[i]
time1[i] <- baseline[i] * effect_modifier + rnorm(1, mean = 0, sd = 3)
} else {
# Control group: small random change
time1[i] <- baseline[i] + rnorm(1, mean = 0, sd = 3)
}
}
# Calculate time2 values
time2 <- vector("numeric", n)
for (i in 1:n) {
if (group[i] == 1) {
# Treatment group: additional effect proportional to time1
effect_modifier <- 1 + prop_effect2 + 0.05 * severity[i]
time2[i] <- time1[i] * effect_modifier + rnorm(1, mean = 0, sd = 3)
} else {
# Control group: small random change
time2[i] <- time1[i] + rnorm(1, mean = 0, sd = 3)
}
}
# Save time2 values prior to censoring to create a model without censoring
time2_all <- time2
# Generate censoring indicators
# Higher probability of censoring (dropout) for:
# 1. Sicker patients (higher severity)
# 2. Control group participants (treatment has benefit that keeps people engaged)
# 3. Those with smaller improvements from baseline to time1
# Calculate change from baseline to time1
change_time1 <- time1 - baseline
# Calculate censoring probabilities
# Logit model for censoring
logit_censoring <- -2 +
1.5 * severity + # Sicker patients more likely to drop out
-0.5 * group + # Treatment group less likely to drop out
-0.05 * change_time1 # Those improving less likely to drop out
# Convert to probability
p_censored <- 1 / (1 + exp(-logit_censoring))
# Generate censoring indicator (1 = censored/dropped out, 0 = observed)
censored <- rbinom(n, 1, p_censored)
# Apply censoring to the data (set values to NA)
time2[censored == 1] <- NA
# Create data frame
data <- data.frame(
id = 1:n,
group = factor(group, labels = c("Control", "Treatment")),
baseline = baseline,
time1 = time1,
time2 = time2,
time2_all = time2_all,
severity = severity, # Include the latent severity variable
censored = censored # Include the censoring indicator
)
# Create change scores for the full dataset
data$change1 <- data$time1 - data$baseline
data$change2 <- data$time2 - data$baseline
data
Analyze Ideal Dataset with no Loss to Follow-Up
Let’s analyze this dataset using the difference in group means:
group_means <- data %>% group_by(group) %>% summarize(mean(time2_all))
group_means
The average treatment effect when there is no loss to follow-up is
and we do not adjust for baseline, we see:
group_means %>% deframe %>% .[["Treatment"]] - group_means %>% deframe %>% .[["Control"]]
Using ANCOVA we get:
ideal <- glm(time2_all ~ baseline + group, data = data)
summary(ideal)
The effect estimate here is 17.65, which is higher than the
difference in group means of 16.38.
When there is loss to follow-up, the näive approach to estimating the
average treatment effect is to ignore patients who were censored, which
means removing them from the data. The average treatment effect is then
the mean outcome (time2) of the treated group minus the mean outcome
(time2) of the untreated in the remaining patients. Here we return to
using time2, which has missing values for censored patients.
group_means_uncensored <- data %>% filter(censored == 0) %>% group_by(group) %>% summarize(mean(time2))
group_means_uncensored
Ignoring censored patients underestimates the treatment effect,
because patients who have higher baseline values have higher treatment
effects, but are also more likely to drop out (be lost to
follow-up):
group_means_uncensored %>% deframe %>% .[["Treatment"]] - group_means_uncensored %>% deframe %>% .[["Control"]]
We can also use logistic regression to do the same näive analysis,
using the ANCOVA approach of adjusting for baseline.
model_naive <- lm(time2 ~ group + baseline,
data = subset(data, !is.na(time2)))
model_naive
This result is closer to the true result of 17.65
One appropriate way to analyze an RCT with censored patients is by
using IP (Inverse Probability) weighting to adjust the data for the
probability of remaining in the study. This creates a pseudo-population
where the outcome for patients who were uncensored gets weighted by
their inverse probability of staying in the study as calculated using
baseline covariates.
To do this, we first use logistic regression to create a model that
predicts whether a patient will be censored based on group (the
treatment) and baseline HBA1C.
# Model for censoring process - modeling factors affecting dropout
model_censoring <- glm(censored ~ group + baseline + time1,
data = data,
family = binomial(link = "logit"))
summary(model_censoring)
The model for censoring is consistent with the data generating
process in which there is a Higher probability of censoring (dropout)
for:
Sicker patients (higher severity)
Control group participants (treatment has benefit that keeps
people engaged)
Those with smaller improvements from baseline to time1
Now we can predict the probability of remaining in the study (not
being censored) by using the regression results on each patient in the
study and calculating 1 - p(censored) Look at the probability of
remaining in the study to convince yourself that the model is producing
appropriate results. For example, patients in the treatment group are
more likely to remain in the study, as expected.
data$p_observed <- 1 - predict(model_censoring, type = "response")
data
We can compare covariates based on treatment group. Notice that those
in the control group are more likely to be censored and therefore have a
lower probability of being observed at time2:
table2 <- tbl_summary(data, by = group) |> add_p() |> bold_labels()
table2
Now we add the IP weights to the data
# Create weights as inverse of probability of being observed
data$ipw <- 1 / data$p_observed
Finally, we can do a second regression with the IP weights to
estimate the average treatment effect:
# Weighted analysis
model_ipw <- glm(time2 ~ group + baseline,
data = subset(data, !is.na(time2)),
weights = ipw)
summary(model_ipw)
FInally we use this model and G-Computation to create the
counterfactuals (the outcomes when treated and untreaded) for all
patients.
To do this, we copy the dataset, set all patients to the Treatment
group, then use the model to predict time2_1. Then we do the same for
the control to create time2_0. The difference in means give us the
effect.
# Copy dataset
datap <- data
# Set all patients to Treatment
datap <- datap %>% mutate(group = "Treatment")
# Predict and store time2_1
time2_1 <- predict(model_ipw, newdata = datap, type = "response")
# Set all patients to Control
datap <- datap %>% mutate(group = "Control")
# Predict time2_0 (for control)
time2_0 <- predict(model_ipw, newdata = datap, type = "response")
# Find the difference in means
mean(time2_1) - mean(time2_0)
This estimand does not adjust the outcome for baseline scores.
Analyzing Censored Dataset
# Create interaction term for the model - do this BEFORE creating the uncensored subset
data$group_numeric <- as.numeric(data$group) - 1 # Convert to 0/1
data$baseline_by_group <- data$baseline * data$group_numeric
# Create uncensored subset - AFTER creating all derived variables
data_uncensored <- subset(data, censored == 0)
print(paste("Number of censored observations:", sum(data$censored)))
print(paste("Proportion of censored observations:", mean(data$censored)))
# Basic visualization - compare baseline by censoring status
ggplot(data, aes(x = factor(censored), y = baseline, fill = group)) +
geom_boxplot() +
ggtitle("Baseline Scores by Group and Censoring Status") +
xlab("Censored (1 = yes, 0 = no)") +
ylab("Baseline Score")
# Create change scores
data$change1 <- data$time1 - data$baseline
data$change2 <- data$time2 - data$baseline
# Longitudinal plot with censoring indicated
data_long <- data %>%
pivot_longer(cols = c(baseline, time1, time2),
names_to = "time",
values_to = "score") %>%
mutate(time = factor(time, levels = c("baseline", "time1", "time2"),
labels = c("Baseline", "Time 1", "Time 2")))
# Add transparency based on censoring (censored data points are more transparent)
ggplot(data_long, aes(x = time, y = score, group = interaction(id, group),
color = group, alpha = factor(censored))) +
geom_line() +
scale_alpha_manual(values = c("0" = 1, "1" = 0.2), name = "Censored") +
stat_summary(data = subset(data_long, !(time == "Time 2" & is.na(score))),
aes(group = group), fun = mean, geom = "line", size = 1.5) +
stat_summary(data = subset(data_long, !(time == "Time 2" & is.na(score))),
aes(group = group), fun = mean, geom = "point", size = 3) +
theme_minimal() +
ggtitle("Change in Scores Over Time by Group (with Censoring)") +
ylab("Score") +
xlab("Measurement Time")
# Define SEM model for uncensored data
# We need a separate model because the censoring variable has no variance in the uncensored subset
model_uncens <- '
# Latent variable for true change
change =~ change1 + change2
# Regression paths: group, baseline, and severity predict change
# Group represents treatment effect
# Baseline represents the proportional nature of the effect
# Severity represents the hidden confounding variable
change ~ group + baseline + severity
# Allow residual correlation between time points
change1 ~~ change2
'
# Fit the model using only uncensored data (creates bias)
fit_uncens <- sem(model_uncens, data = data_uncensored)
summary(fit_uncens, fit.measures = TRUE, standardized = TRUE)
# Model that ignores severity (misspecified)
model_misspec <- '
# Latent variable for true change
change =~ change1 + change2
# Regression paths: only group and baseline predict change
# Missing the severity variable (creates omitted variable bias)
change ~ group + baseline
# Allow residual correlation between time points
change1 ~~ change2
'
# Fit the misspecified model with all data using FIML
fit_misspec <- sem(model_misspec, data = data, missing = "fiml")
summary(fit_misspec, fit.measures = TRUE, standardized = TRUE)
# Fit the misspecified model with uncensored data only (double bias)
fit_misspec_uncens <- sem(model_misspec, data = data_uncensored)
summary(fit_misspec_uncens, fit.measures = TRUE, standardized = TRUE)
# Compare treatment effect estimates across models
# We need to handle the case where some models may not have been successfully fit
treatmentEffect <- data.frame(
Model = character(),
Estimate = numeric(),
stringsAsFactors = FALSE
)
# Extract treatment effect from model1
if(exists("fit_cens")) {
idx <- which(parameterEstimates(fit_cens)$lhs == "change" &
parameterEstimates(fit_cens)$rhs == "group")
if(length(idx) > 0) {
treatmentEffect <- rbind(treatmentEffect,
data.frame(Model = "Complete + Severity",
Estimate = parameterEstimates(fit_cens)[idx, "est"]))
}
}
# Extract from model with uncensored data + severity
if(exists("fit_uncens")) {
idx <- which(parameterEstimates(fit_uncens)$lhs == "change" &
parameterEstimates(fit_uncens)$rhs == "group")
if(length(idx) > 0) {
treatmentEffect <- rbind(treatmentEffect,
data.frame(Model = "Uncensored + Severity",
Estimate = parameterEstimates(fit_uncens)[idx, "est"]))
}
}
# Extract from misspecified model
if(exists("fit_misspec")) {
idx <- which(parameterEstimates(fit_misspec)$lhs == "change" &
parameterEstimates(fit_misspec)$rhs == "group")
if(length(idx) > 0) {
treatmentEffect <- rbind(treatmentEffect,
data.frame(Model = "Complete w/o Severity",
Estimate = parameterEstimates(fit_misspec)[idx, "est"]))
}
}
# Extract from misspecified model with uncensored data
if(exists("fit_misspec_uncens")) {
idx <- which(parameterEstimates(fit_misspec_uncens)$lhs == "change" &
parameterEstimates(fit_misspec_uncens)$rhs == "group")
if(length(idx) > 0) {
treatmentEffect <- rbind(treatmentEffect,
data.frame(Model = "Uncensored w/o Severity",
Estimate = parameterEstimates(fit_misspec_uncens)[idx, "est"]))
}
}
# Print the comparison table
print("Treatment Effect Estimates Across Models:")
print(treatmentEffect)
# This comparison table shows the bias introduced by analyzing only uncensored participants
# and by omitting important confounding variables from the model
# Alternative model with latent variables for each time point
# For proportional effects, we need interaction terms
model2 <- '
# Latent variables for each time point
baseline =~ 1*baseline
followup1 =~ 1*time1
followup2 =~ 1*time2
# Regression paths from baseline to follow-ups
followup1 ~ baseline
followup2 ~ baseline + followup1
# Effect of treatment on follow-ups
# Direct effects of group
followup1 ~ group
followup2 ~ group
# Interaction effects (proportional effects)
# Create interaction term in the data first
'
# Alternative model with latent variables and missing data handling
model2_cens <- '
# Latent variables for each time point - avoid name collisions with observed variables
latent_baseline =~ 1*baseline
latent_followup1 =~ 1*time1
latent_followup2 =~ 1*time2
# Regression paths from baseline to follow-ups
latent_followup1 ~ latent_baseline
latent_followup2 ~ latent_baseline + latent_followup1
# Effect of treatment on follow-ups
# Direct effects of group
latent_followup1 ~ group
latent_followup2 ~ group
# Severity affects outcomes
latent_baseline ~ severity
latent_followup1 ~ severity
latent_followup2 ~ severity
# Censoring is related to severity and treatment
censored ~ severity + group
# Interaction effects (proportional effects)
latent_followup1 ~ baseline_by_group
latent_followup2 ~ baseline_by_group
'
# Create interaction term for the model if not already there
if(!"baseline_by_group" %in% names(data)) {
data$group_numeric <- as.numeric(data$group) - 1 # Convert to 0/1
data$baseline_by_group <- data$baseline * data$group_numeric
}
# Fit the censoring-aware model with FIML
fit2_cens <- sem(model2_cens, data = data, missing = "fiml")
summary(fit2_cens, fit.measures = TRUE, standardized = TRUE)
# Visualize the censoring-aware model
semPaths(fit2_cens, what = "est", fade = FALSE, residuals = FALSE,
edge.label.cex = 0.8,
title = FALSE,
layout = "tree2")
# Create a simplified version for uncensored data only
model2_simple <- '
# Latent variables for each time point - avoid name collisions
latent_baseline =~ 1*baseline
latent_followup1 =~ 1*time1
latent_followup2 =~ 1*time2
# Regression paths from baseline to follow-ups
latent_followup1 ~ latent_baseline
latent_followup2 ~ latent_baseline + latent_followup1
# Effect of treatment on follow-ups
# Direct effects of group
latent_followup1 ~ group
latent_followup2 ~ group
# Interaction effects (proportional effects)
latent_followup1 ~ baseline_by_group
latent_followup2 ~ baseline_by_group
'
# Fit the simplified model with uncensored data only
# Check first that required variables exist in the dataset
print("Variables in data_uncensored:")
print(names(data_uncensored))
# Verify that baseline_by_group exists in the dataset
if("baseline_by_group" %in% names(data_uncensored)) {
fit2_uncens <- sem(model2_simple, data = data_uncensored)
summary(fit2_uncens, fit.measures = TRUE, standardized = TRUE)
} else {
print("Error: baseline_by_group missing from data_uncensored")
# Create it again just to be sure
data_uncensored$group_numeric <- as.numeric(data_uncensored$group) - 1
data_uncensored$baseline_by_group <- data_uncensored$baseline * data_uncensored$group_numeric
# Now try fitting the model
fit2_uncens <- sem(model2_simple, data = data_uncensored)
summary(fit2_uncens, fit.measures = TRUE, standardized = TRUE)
}
# Interpretation
# For data with censoring and proportional treatment effects:
#
# In this simulation:
#
# 1. SELECTION BIAS MECHANISM:
# - We created a "severity" latent variable that affects:
# a) Baseline scores (sicker patients have lower scores)
# b) Treatment effect (treatment works better for sicker patients)
# c) Censoring probability (sicker patients more likely to drop out)
# - Treatment group participants are less likely to be censored
# - Participants with less improvement are more likely to be censored
#
# 2. BIAS IN THE TREATMENT EFFECT ESTIMATE:
# - Complete model with severity included (fit_cens): Most accurate estimate
# - Uncensored data with severity included (fit_uncens): Biased estimate
# (typically underestimates treatment effect because sicker patients
# who would benefit more are more likely to drop out)
# - Models without severity (fit_misspec & fit_misspec_uncens): Biased due to
# omitted variable bias (severity affects both censoring and outcomes)
#
# 3. THE BIASES INTERACT:
# - Selection bias: Analyzing only uncensored participants
# - Omitted variable bias: Not including severity variable
# - The combined effect typically leads to underestimating treatment efficacy
#
# 4. PROPER HANDLING APPROACHES:
# - Full Information Maximum Likelihood (FIML) for missing data
# - Including all relevant confounders in the model
# - Sensitivity analyses for different missing data mechanisms
# - Inverse probability weighting to account for selection bias
#
# The comparison of treatment effect estimates across models shows how much
# the estimated effect changes when using different approaches, demonstrating
# the magnitude of bias from censoring and model misspecification.
# GLM Analysis for Treatment Effect with Censoring
# Assuming the data has already been generated as in the previous code
# and variables are in the 'data' dataframe
# 1. Naive GLM analysis ignoring censoring
# This ignores the missing data problem and just analyzes what is observed
# Model for time2 outcome, complete cases only
model_naive <- glm(time2 ~ group + baseline,
data = subset(data, !is.na(time2)),
family = gaussian())
# Summary of naive model
summary(model_naive)
# 2. Model for change from baseline
# Calculate absolute change and use as outcome
data$abs_change <- data$time2 - data$baseline
model_change <- glm(abs_change ~ group + baseline,
data = subset(data, !is.na(time2)),
family = gaussian())
summary(model_change)
# 3. Model for proportional change
# Calculate proportional change and use as outcome
data$prop_change <- (data$time2 - data$baseline) / data$baseline
model_prop_change <- glm(prop_change ~ group + baseline,
data = subset(data, !is.na(time2)),
family = gaussian())
summary(model_prop_change)
# 4. Model that includes severity (the hidden confounder)
model_with_severity <- glm(time2 ~ group + baseline + severity,
data = subset(data, !is.na(time2)),
family = gaussian())
summary(model_with_severity)
# 5. Model for censoring process - modeling factors affecting dropout
model_censoring <- glm(censored ~ group + baseline + severity,
data = data,
family = binomial(link = "logit"))
summary(model_censoring)
# 6. Inverse probability weighting to account for censoring
# First, predict probability of being observed (not censored)
data$p_observed <- 1 - predict(model_censoring, type = "response")
# Create weights as inverse of probability of being observed
data$ipw <- 1 / data$p_observed
# Weighted analysis
model_ipw <- glm(time2 ~ group + baseline,
data = subset(data, !is.na(time2)),
family = gaussian(),
weights = ipw)
summary(model_ipw)
# 7. Model with interaction between baseline and treatment
model_interaction <- glm(time2 ~ group * baseline + severity,
data = subset(data, !is.na(time2)),
family = gaussian())
summary(model_interaction)
# 8. Compare treatment effect estimates across different models
# Extract coefficients for the treatment effect (group)
treatment_effects <- data.frame(
Model = c(
"Naive (Complete Cases)",
"Change Score",
"Proportional Change",
"With Severity",
"IPW for Censoring",
"With Interaction"
),
Estimate = c(
coef(model_naive)["groupTreatment"],
coef(model_change)["groupTreatment"],
coef(model_prop_change)["groupTreatment"],
coef(model_with_severity)["groupTreatment"],
coef(model_ipw)["groupTreatment"],
coef(model_interaction)["groupTreatment"]
),
SE = c(
summary(model_naive)$coefficients["groupTreatment", "Std. Error"],
summary(model_change)$coefficients["groupTreatment", "Std. Error"],
summary(model_prop_change)$coefficients["groupTreatment", "Std. Error"],
summary(model_with_severity)$coefficients["groupTreatment", "Std. Error"],
summary(model_ipw)$coefficients["groupTreatment", "Std. Error"],
summary(model_interaction)$coefficients["groupTreatment", "Std. Error"]
)
)
treatment_effects$Lower_CI <- treatment_effects$Estimate - 1.96 * treatment_effects$SE
treatment_effects$Upper_CI <- treatment_effects$Estimate + 1.96 * treatment_effects$SE
# Print comparison table
print(treatment_effects)
# 9. Visualization of treatment effects across models
library(ggplot2)
ggplot(treatment_effects, aes(x = Model, y = Estimate)) +
geom_point(size = 3) +
geom_errorbar(aes(ymin = Lower_CI, ymax = Upper_CI), width = 0.2) +
coord_flip() +
theme_minimal() +
ggtitle("Treatment Effect Estimates Across Different GLM Models") +
ylab("Estimated Treatment Effect") +
xlab("")
# 10. Calculate marginal effects of treatment at different baseline values
# This shows how the treatment effect varies with baseline
# if(requireNamespace("margins", quietly = TRUE)) {
# library(margins)
#
# # For the interaction model
# marg_effects <- margins(model_interaction,
# variables = "group",
# at = list(baseline = seq(min(data$baseline, na.rm = TRUE),
# max(data$baseline, na.rm = TRUE),
# length.out = 10)))
#
# summary(marg_effects)
#
# # Plot the marginal effects
# plot(marg_effects)
# } else {
# Manual calculation of marginal effects at different baseline values
baseline_values <- seq(min(data$baseline, na.rm = TRUE),
max(data$baseline, na.rm = TRUE),
length.out = 10)
# Extract coefficients
b_group <- coef(model_interaction)["groupTreatment"]
b_interaction <- coef(model_interaction)["groupTreatment:baseline"]
# Calculate marginal effect at each baseline value
marginal_effects <- data.frame(
baseline = baseline_values,
effect = b_group + b_interaction * baseline_values
)
# Plot the marginal effects
ggplot(marginal_effects, aes(x = baseline, y = effect)) +
geom_line() +
geom_point() +
theme_minimal() +
ggtitle("Marginal Effect of Treatment at Different Baseline Values") +
ylab("Treatment Effect") +
xlab("Baseline Value")
#}
# 11. Calculate the total effect of treatment on time2
# For models without interaction, this is just the coefficient of group
# For models with interaction, we need to evaluate at the mean baseline
# Mean baseline value
mean_baseline <- mean(data$baseline, na.rm = TRUE)
# Total effect in interaction model
total_effect <- coef(model_interaction)["groupTreatment"] +
coef(model_interaction)["groupTreatment:baseline"] * mean_baseline
cat("Total effect of treatment on time2 at mean baseline:", total_effect, "\n")
# 12. Predicted outcomes for treatment vs control
# Create prediction data for a range of baseline values
pred_data <- expand.grid(
group = factor(c("Control", "Treatment"), levels = levels(data$group)),
baseline = seq(min(data$baseline, na.rm = TRUE),
max(data$baseline, na.rm = TRUE),
length.out = 20),
severity = mean(data$severity, na.rm = TRUE) # Fix severity at mean
)
# Predictions from different models
pred_data$pred_naive <- predict(model_naive, newdata = pred_data, type = "response")
pred_data$pred_with_severity <- predict(model_with_severity, newdata = pred_data, type = "response")
pred_data$pred_interaction <- predict(model_interaction, newdata = pred_data, type = "response")
# Plot predictions
ggplot(pred_data, aes(x = baseline, y = pred_interaction, color = group)) +
geom_line() +
theme_minimal() +
ggtitle("Predicted Time2 Values by Treatment Group and Baseline") +
ylab("Predicted Time2") +
xlab("Baseline Value") +
scale_color_manual(values = c("blue", "red")) +
theme(legend.title = element_blank())
# Additional analysis: Marginal effects for IPW interaction model
model_interaction_ipw <- glm(time2 ~ group * baseline + severity,
data = subset(data, !is.na(time2)),
family = gaussian(),
weights = ipw)
if(requireNamespace("margins", quietly = TRUE)) {
library(margins)
# Marginal effects for standard interaction model
marg_effects_std <- margins(model_interaction,
variables = "group",
at = list(baseline = seq(min(data$baseline, na.rm = TRUE),
max(data$baseline, na.rm = TRUE),
length.out = 10)))
# Marginal effects for IPW interaction model
marg_effects_ipw <- margins(model_interaction_ipw,
variables = "group",
at = list(baseline = seq(min(data$baseline, na.rm = TRUE),
max(data$baseline, na.rm = TRUE),
length.out = 10)))
# Combine for comparison
marg_combined <- data.frame(
baseline = attr(marg_effects_std, "at")$baseline,
effect_std = summary(marg_effects_std)$AME,
effect_ipw = summary(marg_effects_ipw)$AME
)
# Plot comparison of marginal effects
ggplot(marg_combined) +
geom_line(aes(x = baseline, y = effect_std, color = "Standard Model")) +
geom_line(aes(x = baseline, y = effect_ipw, color = "IPW Model")) +
geom_point(aes(x = baseline, y = effect_std, color = "Standard Model")) +
geom_point(aes(x = baseline, y = effect_ipw, color = "IPW Model")) +
theme_minimal() +
ggtitle("Marginal Effect of Treatment at Different Baseline Values") +
ylab("Treatment Effect") +
xlab("Baseline Value") +
scale_color_manual(values = c("blue", "red"), name = "Model Type")
} else {
# Manual calculation for both models
baseline_values <- seq(min(data$baseline, na.rm = TRUE),
max(data$baseline, na.rm = TRUE),
length.out = 10)
# Extract coefficients for standard model
b_group_std <- coef(model_interaction)["groupTreatment"]
b_interaction_std <- coef(model_interaction)["groupTreatment:baseline"]
# Extract coefficients for IPW model
b_group_ipw <- coef(model_interaction_ipw)["groupTreatment"]
b_interaction_ipw <- coef(model_interaction_ipw)["groupTreatment:baseline"]
# Calculate marginal effects
marginal_effects <- data.frame(
baseline = baseline_values,
effect_std = b_group_std + b_interaction_std * baseline_values,
effect_ipw = b_group_ipw + b_interaction_ipw * baseline_values
)
# Plot comparison
ggplot(marginal_effects) +
geom_line(aes(x = baseline, y = effect_std, color = "Standard Model")) +
geom_line(aes(x = baseline, y = effect_ipw, color = "IPW Model")) +
geom_point(aes(x = baseline, y = effect_std, color = "Standard Model")) +
geom_point(aes(x = baseline, y = effect_ipw, color = "IPW Model")) +
theme_minimal() +
ggtitle("Marginal Effect of Treatment at Different Baseline Values") +
ylab("Treatment Effect") +
xlab("Baseline Value") +
scale_color_manual(values = c("blue", "red"), name = "Model Type")
}
Analyze non-censored version, including severity. When no patients
are censored severity should not be needed.
glm(formula = time2_all ~ group + baseline + severity, family = gaussian(),
data = data)
Removing severity from the model
glm(formula = time2_all ~ group + baseline, family = gaussian(),
data = data)
Adding baseline as an interaction with treatment group
glm(formula = time2_all ~ group*baseline, family = gaussian(),
data = data)
Analysis using stabilized inverse probability weights to adjust for
censoring.
Treatment is randomized in this data, so no need to adjust for
treatment assignment
### Estimate stabilized inverse probability weights for censoring ###
# Fit a pooled logistic model that predicts the probability of remaining uncensored
# (not being lost to follow-up)
psc.denom <- glm(censored == 0 ~ group + baseline + severity ,
family=binomial(link="logit"),
data=data)
summary(psc.denom)
# Obtain predicted probabilities for the denominator
data$psc.denom <- predict(psc.denom, data, type="response")
# Fit model for the numerator of the stabilized weights
psc.num <- glm(censored==0 ~ group,
family=binomial(link="logit"), data=data)
summary(psc.num)
# Estimate stabilized weights
data$psc.num <- predict(psc.num, data, type="response")
data <- data %>%
group_by(id) %>%
mutate(
sw_c = cumprod(psc.num)/cumprod(psc.denom)
) %>%
ungroup() %>%
mutate(sw_c = ifelse(is.na(censored), 1, sw_c))
### Min, 25th percentile, median, mean, SD, 75th percentile, and max: stabilized weights ###
summary(data$sw_c)
sd(data$sw_c)
Note that the echo = FALSE parameter was added to the
code chunk to prevent printing of the R code that generated the
plot.
Tennant, Peter W G, Kellyn F Arnold, George T H Ellison, and Mark S
Gilthorpe. 2021.
“Analyses of ‘Change
Scores’ Do Not Estimate Causal Effects in Observational
Data.” International Journal of Epidemiology 51 (5):
1604–15.
https://doi.org/10.1093/ije/dyab050.
LS0tCnRpdGxlOiAiQ2xhdWRlIENoYW5nZSBCYXNlbGluZSBDZW5zb3JpbmciCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCmRhdGU6ICIyMDI1LTA0LTIxIgpiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKYGBgCgpgYGB7cn0KcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImp0ZXh0b3IvZGFnaXR0eS9yIikKbGlicmFyeShkYWdpdHR5KQpsaWJyYXJ5KGdnZGFnKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKYGBgCgpUaGUgZ29hbCBvZiB0aGlzIG5vdGVib29rIGlzIHRvIGV4cGxhaW4gaG93IHRvIGVzdGltYXRlIHRoZSBhdmVyYWdlIHRyZWF0bWVudCBlZmZlY3Qgb2YgYSBkaWNob3RvbW91cyAoYmluYXJ5KSB0cmVhdG1lbnQgb24gY2hhbmdlIGZyb20gYSBjb250aW51b3VzIGJhc2VsaW5lIG1lYXN1cmUgW0B0ZW5uYW50MjAyMV0gdW5kZXIgdGhyZWUgZGlmZmVyZW50IHNjZW5hcmlvczoKCjEuICBBIFJhbmRvbWl6ZWQgQ29udHJvbGxlZCBUcmlhbCAoUkNUKSB3aXRoIG5vIGxvc3MgdG8gZm9sbG93LXVwLgoyLiAgQW4gUkNUIHdpdGggbG9zcyB0byBmb2xsb3ctdXAuCjMuICBBbiBvYnNlcnZhdGlvbmFsIHN0dWR5IHdpdGggdHJlYXRtZW50IGNvbmZvdW5kZXJzIGFuZCBsb3NzIHRvIGZvbGxvdy11cC4KCkVhY2ggYXBwcm9hY2ggdXNlcyB0aGUgc2FtZSBjb3JlIGRhdGEgZ2VuZXJhdGluZyBtb2RlbCB3aXRoIGEgcHJvcG9ydGlvbmFsIHRyZWF0bWVudCBlZmZlY3QsIHdoZXJlIHRoZSB0cmVhdG1lbnQgd29ya3MgYmV0dGVyIGluIHBhdGllbnRzIHdpdGggbG93ZXIgYmFzZWxpbmUgc2NvcmVzLgoKIyMgUmFuZG9taXplZCBDb250cm9sbGVkIFRyaWFsIFdpdGggTm8gTG9zcyB0byBGb2xsb3ctVXAKCkluIHRoaXMgc2ltdWxhdGlvbjoKClwtIGBzZXZlcml0eWAgaXMgYSBsYXRlbnQgKHVub2JzZXJ2ZWQpIHZhcmlhYmxlIHRoYXQgYWZmZWN0czoKCmEpICBCYXNlbGluZSBzY29yZXMgKHNpY2tlciBwYXRpZW50cyBoYXZlIGxvd2VyIHNjb3JlcykKCmIpICBUcmVhdG1lbnQgZWZmZWN0ICh0cmVhdG1lbnQgd29ya3MgYmV0dGVyIGZvciBzaWNrZXIgcGF0aWVudHMpCgpOb3RlIHRoYXQgaW4gdGhpcyBtb2RlbCBzZXZlcml0eSBpcyBub3QgYWZmZWN0ZWQgYnkgdHJlYXRtZW50IG9yIHRpbWUuIEl0IGlzIHNldCBhdCBiYXNlbGluZSBmb3IgZWFjaCBwYXRpZW50IGFuZCByZW1haW5zIHRoZSBzYW1lIGZvciBlYWNoIHRpbWUgcGVyaW9kLgoKVGhlIENhdXNhbCBEQUcgZm9yIHRoaXMgZGF0YSBnZW5lcmF0aW5nIG1vZGVsIGlzIHNob3duIGJlbG93LgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOH0KCiMgQ3JlYXRlIHRoZSBEQUcgdXNpbmcgZGFnaWZ5CmRhZ1JDVCA8LSBkYWdpZnkoCiAgYmFzZWxpbmUgfiBzZXZlcml0eSwKICB0aW1lMSB+IGJhc2VsaW5lICsgZ3JvdXAgKyBzZXZlcml0eSwKICBjaGFuZ2UxIH4gYmFzZWxpbmUgKyB0aW1lMSwKICB0aW1lMiB+IGdyb3VwICsgdGltZTEgKyBzZXZlcml0eSwKICBjaGFuZ2UyIH4gYmFzZWxpbmUgKyB0aW1lMiwKICBleHBvc3VyZSA9ICJncm91cCIsCiAgb3V0Y29tZSA9ICJ0aW1lMiIsCiAgY29vcmRzID0gZGF0YS5mcmFtZSgKICAgIG5hbWUgPSBjKCJiYXNlbGluZSIsICJzZXZlcml0eSIsICJncm91cCIsICJ0aW1lMSIsICJjaGFuZ2UxIiwgInRpbWUyIiwgImNoYW5nZTIiKSwKICAgIHggPSBjKDAsIDAsIDQsIDMsIDEuNSwgNS41LCA0KSwKICAgIHkgPSBjKDEsIDIsIDIsIDEsIDAsIDEsIDApCiAgKQopCgojIENoYW5nZSBzdGF0dXMgb2Ygc2V2ZXJpdHkgdG8gaW5kaWNhdGUgdGhhdCBpdCBpcyBhIGxhdGVudCB2YXJpYWJsZQpsYXRlbnRzKGRhZ1JDVCkgPSAic2V2ZXJpdHkiCgojIENvbnZlcnQgdGhlIGRhZ2l0dHkgZ3JhcGggdG8gYSBnZ2RhZyBncm91cCBmb3IgcHJldHRpZXIgcHJpbnRpbmcKdGlkeV9kYWdSQ1QgPC0gdGlkeV9kYWdpdHR5KGRhZ1JDVCkgCgojIFBsb3Qgd2l0aCBjdXN0b20gbm9kZSBzaGFwZXMKIyBHZXQgdGhlIHN0YXR1cyAoZXhwb3N1cmUsIG91dGNvbWUpIGluZm9ybWF0aW9uIGFuZCBzZXQgdmFyaWFibGVzIHdpdGhvdXQgYSBzdGF0dXMgdG8gIm9ic2VydmVkIgpzdGF0dXNfZGF0YSA8LSBnZ2RhZ19zdGF0dXModGlkeV9kYWdSQ1QpJGRhdGEgJT4lIG11dGF0ZShzdGF0dXMgPSBpZmVsc2UoaXMubmEoc3RhdHVzKSwgIm9ic2VydmVkIiwgYXMuY2hhcmFjdGVyKHN0YXR1cykpKQoKI3N0YXR1c19kYXRhIDwtIHN0YXR1c19kYXRhICU+JSBtdXRhdGUoZ3N0YXR1cyA9IGFzLmNoYXJhY3RlcihzdGF0dXMpLCBnc3RhdHVzID0gaWZlbHNlKG5hbWUgPT0gImNlbnNvcmVkIiwgImFkanVzdGVkIiwgYXMuY2hhcmFjdGVyKHN0YXR1cykpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3N0YXR1cyA9IGZhY3Rvcihnc3RhdHVzLGxldmVscyA9IGMobGV2ZWxzKHN0YXR1cyksICJhZGp1c3RlZCIpKSkKCiMgUGxvdCB3aXRoIGN1c3RvbSBub2RlIHNoYXBlcyB3aGlsZSBwcmVzZXJ2aW5nIHN0YXR1cyBjb2xvcmluZwpnZ3Bsb3Qoc3RhdHVzX2RhdGEsIGFlcyh4ID0geCwgeSA9IHksIHhlbmQgPSB4ZW5kLCB5ZW5kID0geWVuZCkpICsKICBnZW9tX2RhZ19lZGdlcyhhcnJvd19kaXJlY3RlZCA9IGdyaWQ6OmFycm93KGxlbmd0aCA9IHVuaXQoMC41LCAiY20iKSwgdHlwZSA9ICJjbG9zZWQiKSkgKwogIGdlb21fZGFnX3BvaW50KGFlcyhjb2xvciA9IHN0YXR1cyksIHNpemUgPSAyNCkgKwogIGdlb21fZGFnX3RleHQoY29sb3IgPSAid2hpdGUiKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoY2lyY2xlID0gMTYsIHNxdWFyZSA9IDE1KSwgZ3VpZGUgPSAibm9uZSIpICsKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShuYW1lID0gIlN0YXR1cyIsIG5hLnZhbHVlID0gImdyZXk1MCIpICsKICB0aGVtZV9kYWcoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikKCgpgYGAKCk5vdyBsZXQncyBzaW11bGF0ZSBkYXRhIGZvciB0aGUgUkNULiBUaGlzIHNpbXVsYXRlcyBhbmQgcmV0YWlucyB0aGUgcG90ZW50aWFsIG91dGNvbWVzIFkwX3RpbWUxLCBZMV90aW1lMSwgWTBfdGltZTIgYW5kIFkxX3RpbWUyIGZvciBhbGwgcGF0aWVudHMsIHRoZW4gc2V0cyB0aW1lMSBhbmQgdGltZTIgZm9yIGEgcGF0aWVudCBiYXNlZCBvbiB0aGF0IHBhdGllbnQncyBhc3NpZ25lZCB0cmVhdG1lbnQgaW4gdGhlIGRhdGEuIFRoaXMgbWVhbnMgdGhhdCB3ZSBjYW4gY2FsY3VsYXRlIHRoZSB0cnVlIGNhdXNhbCBlZmZlY3QgZnJvbSB0aGUgcG90ZW50aWFsIG91dGNvbWVzLgoKYGBge3J9CiMgU2V0IHNlZWQgZm9yIHJlcHJvZHVjaWJpbGl0eQpzZXQuc2VlZCgxMjQpCgojIFNpbXVsYXRlIGRhdGEgZm9yIGRlbW9uc3RyYXRpb24KIyAxNTAgcGFydGljaXBhbnRzLCAyIGdyb3VwcyAodHJlYXRtZW50IGFuZCBjb250cm9sKQpuIDwtIDE1MApncm91cCA8LSByZXAoYygwLCAxKSwgZWFjaCA9IG4vMikgICMgMCA9IGNvbnRyb2wsIDEgPSB0cmVhdG1lbnQKCiMgR2VuZXJhdGUgYmFzZWxpbmUgc2NvcmVzIChzaW1pbGFyIGZvciBib3RoIGdyb3VwcykKYmFzZWxpbmUgPC0gcm5vcm0obiwgbWVhbiA9IDUwLCBzZCA9IDEwKQoKIyBBZGQgYSBsYXRlbnQgInNldmVyaXR5IiB2YXJpYWJsZSB0aGF0IGFmZmVjdHMgYmFzZWxpbmUgc2NvcmUKc2V2ZXJpdHkgPC0gcm5vcm0obiwgbWVhbiA9IDAsIHNkID0gMSkKCiMgQWRqdXN0IGJhc2VsaW5lIGJ5IHNldmVyaXR5IChzaWNrZXIgcGF0aWVudHMgaGF2ZSBsb3dlciBiYXNlbGluZSBzY29yZXMpCmJhc2VsaW5lIDwtIGJhc2VsaW5lIC0gNSAqIHNldmVyaXR5CgojIEdlbmVyYXRlIGZvbGxvdy11cCBzY29yZXMgd2l0aCBwcm9wb3J0aW9uYWwgZWZmZWN0cwpwcm9wX2VmZmVjdDEgPC0gMC4yMCAgIyAyMCUgaW1wcm92ZW1lbnQgYXQgdGltZTEgZm9yIHRyZWF0bWVudCBncm91cApwcm9wX2VmZmVjdDIgPC0gMC4xNSAgIyBBZGRpdGlvbmFsIDE1JSBpbXByb3ZlbWVudCBhdCB0aW1lMiBmb3IgdHJlYXRtZW50IGdyb3VwCgojIENhbGN1bGF0ZSBwb3RlbnRpYWwgb3V0Y29tZXMKIyBUcnVlIGVmZmVjdCBpcyBwcm9wb3J0aW9uYWwgdG8gYmFzZWxpbmUKIyBUaGUgdHJlYXRtZW50IGFsc28gaGFzIGEgc3Ryb25nZXIgZWZmZWN0IGZvciBwYXRpZW50cyB3aXRoIGhpZ2hlciBzZXZlcml0eQpZMF90aW1lMSA8LSBiYXNlbGluZSArIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDMpClkxX3RpbWUxIDwtIGJhc2VsaW5lICogKDEgKyBwcm9wX2VmZmVjdDEgKyAwLjEgKiBzZXZlcml0eSkgKyBybm9ybShuLCBtZWFuID0gMCwgc2QgPSAzKQpZMF90aW1lMiA8LSBZMF90aW1lMSArIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDMpClkxX3RpbWUyIDwtIFkxX3RpbWUxICogKDEgKyBwcm9wX2VmZmVjdDEgKyAwLjEgKiBzZXZlcml0eSkgKyBybm9ybShuLCBtZWFuID0gMCwgc2QgPSAzKQoKIyBDYWxjdWxhdGUgdGltZTEgdmFsdWVzIC0gdHJ1ZSBlZmZlY3QgaXMgcHJvcG9ydGlvbmFsIHRvIGJhc2VsaW5lCiMgVGhlIHRyZWF0bWVudCBhbHNvIGhhcyBhIHN0cm9uZ2VyIGVmZmVjdCBmb3IgcGF0aWVudHMgd2l0aCBoaWdoZXIgc2V2ZXJpdHkKIyB0aW1lMSA8LSB2ZWN0b3IoIm51bWVyaWMiLCBuKQojIGZvciAoaSBpbiAxOm4pIHsKIyAgIGlmIChncm91cFtpXSA9PSAxKSB7CiMgICAgICMgVHJlYXRtZW50IGdyb3VwOiBlZmZlY3QgaXMgcHJvcG9ydGlvbmFsIHRvIGJhc2VsaW5lIGFuZCBpbmNyZWFzZWQgYnkgc2V2ZXJpdHkKIyAgICAgZWZmZWN0X21vZGlmaWVyIDwtIDEgKyBwcm9wX2VmZmVjdDEgKyAwLjEgKiBzZXZlcml0eVtpXQojICAgICB0aW1lMVtpXSA8LSBiYXNlbGluZVtpXSAqIGVmZmVjdF9tb2RpZmllciArIHJub3JtKDEsIG1lYW4gPSAwLCBzZCA9IDMpCiMgICB9IGVsc2UgewojICAgICAjIENvbnRyb2wgZ3JvdXA6IHNtYWxsIHJhbmRvbSBjaGFuZ2UKIyAgICAgdGltZTFbaV0gPC0gYmFzZWxpbmVbaV0gKyBybm9ybSgxLCBtZWFuID0gMCwgc2QgPSAzKQojICAgfQojIH0KCiMgdGltZTIgPC0gdmVjdG9yKCJudW1lcmljIiwgbikKIyBmb3IgKGkgaW4gMTpuKSB7CiMgICBpZiAoZ3JvdXBbaV0gPT0gMSkgewojICAgICAjIFRyZWF0bWVudCBncm91cDogYWRkaXRpb25hbCBlZmZlY3QgcHJvcG9ydGlvbmFsIHRvIHRpbWUxCiMgICAgIGVmZmVjdF9tb2RpZmllciA8LSAxICsgcHJvcF9lZmZlY3QyICsgMC4wNSAqIHNldmVyaXR5W2ldCiMgICAgIHRpbWUyW2ldIDwtIHRpbWUxW2ldICogZWZmZWN0X21vZGlmaWVyICsgcm5vcm0oMSwgbWVhbiA9IDAsIHNkID0gMykKIyAgIH0gZWxzZSB7CiMgICAgICMgQ29udHJvbCBncm91cDogc21hbGwgcmFuZG9tIGNoYW5nZQojICAgICB0aW1lMltpXSA8LSB0aW1lMVtpXSArIHJub3JtKDEsIG1lYW4gPSAwLCBzZCA9IDMpCiMgICB9CiMgfQoKIyBTZXQgdGhlIG9ic2VydmVkIHRpbWUxIGFuZCB0aW1lMiBiYXNlZCBvbiB0cmVhdG1lbnQgKGdyb3VwKSAgYXNzaWdubWVudAp0aW1lMSA8LSBpZmVsc2UoZ3JvdXAgPT0gMCwgWTBfdGltZTEsIFkxX3RpbWUxKQp0aW1lMiA8LSBpZmVsc2UoZ3JvdXAgPT0gMCwgWTBfdGltZTIsIFkxX3RpbWUyKQoKIyBDcmVhdGUgZGF0YSBmcmFtZQpkYXRhIDwtIGRhdGEuZnJhbWUoCiAgaWQgPSAxOm4sCiAgZ3JvdXAgPSBmYWN0b3IoZ3JvdXAsIGxhYmVscyA9IGMoIkNvbnRyb2wiLCAiVHJlYXRtZW50IikpLAogIGJhc2VsaW5lID0gYmFzZWxpbmUsCiAgdGltZTEgPSB0aW1lMSwKICB0aW1lMiA9IHRpbWUyLAogIFkwX3RpbWUxID0gWTBfdGltZTEsCiAgWTFfdGltZTEgPSBZMV90aW1lMSwKICBZMF90aW1lMiA9IFkwX3RpbWUyLAogIFkxX3RpbWUyID0gWTFfdGltZTIsCiAgc2V2ZXJpdHkgPSBzZXZlcml0eSAgIyBJbmNsdWRlIHRoZSBsYXRlbnQgc2V2ZXJpdHkgdmFyaWFibGUKKQoKIyBDcmVhdGUgY2hhbmdlIHNjb3JlcyBmb3IgdGhlIGZ1bGwgZGF0YXNldApkYXRhJGNoYW5nZTEgPC0gZGF0YSR0aW1lMSAtIGRhdGEkYmFzZWxpbmUKZGF0YSRjaGFuZ2UyIDwtIGRhdGEkdGltZTIgLSBkYXRhJGJhc2VsaW5lCmRhdGEKYGBgCgpGb3IgcHVibGljYXRpb24gcmVhZHkgdGFibGVzLCB3ZSBjYW4gdXNlIGd0c3VtbWFyeS4gSGVyZSBpcyB0aGUgY2Fubm9uaWNhbCBUYWJsZSAxOgoKYGBge3J9CmxpYnJhcnkoZ3RzdW1tYXJ5KQp0YWJsZTEgPC0gZGF0YSB8PiB0Ymxfc3VtbWFyeSgpCnRhYmxlMQpgYGAKCkFuZCBoZXJlIGlzIHRoZSBDYW5vbmljYWwgVGFibGUgMi4gVGhpcyBzaG93cyBhIHNpZ25pZmljYW50IGRpZmZlcmVuY2UgYmV0d2VlbiBgdGltZTJgIGZvciB0aGUgdHJlYXRtZW50IGFuZCBjb250cm9sIGdyb3Vwcy4KCmBgYHtyfQp0YWJsZTIgPC0gdGJsX3N1bW1hcnkoZGF0YSwgYnkgPSBncm91cCkgfD4gYWRkX3AoKSB8PiBib2xkX2xhYmVscygpCnRhYmxlMgpgYGAKCldpdGggYWxsIHRoZSBwb3RlbnRpYWwgb3V0Y29tZXMgd2UgY2FuIGNhbGN1bGF0ZSB0aGUgQXZlcmFnZSBUcmVhdG1lbnQgRWZmZWN0IG9mIHRoZSB0cmVhdG1lbnQgb24gdGltZTIgYXM6CgpgYGB7cn0KbWVhbihkYXRhJFkxX3RpbWUyKSAtIG1lYW4oZGF0YSRZMF90aW1lMikKYGBgCgpUaGlzIGlzIGp1c3QgdGhlIG1lYW4gb2YgdGhlIHJhdyBpbmRpdmlkdWFsIHRyZWF0bWVudCBlZmZlY3RzOgoKYGBge3J9Cm1lYW4oZGF0YSRZMV90aW1lMiAtIGRhdGEkWTBfdGltZTIpCmBgYAoKVGhpcyB2YWx1ZSBpcyBub3QgYWRqdXN0ZWQgZm9yIHRoZSBiYXNlbGluZSB2YWx1ZSBvZiBlYWNoIHBhdGllbnQuCgpXZSBjYW4gYWxzbyBjb21wdXRlIHRoZSBkaWZmZXJlbmNlIGluIGNoYW5nZSBzY29yZXMgYmV0d2VlbiB0aGUgdHJlYXRlZCBhbmQgdW50cmVhdGVkLCB3aGljaCBpcyB0aGUgY2F1c2FsIGVzdGltYW5kIHNob3dpbmcgdGhlIGVmZmVjdCBvZiB0cmVhdG1lbnQsICRYJCwgb24gY2hhbmdlIGluIHNjb3JlICRZJCwgd2hlcmUgJFlfMV54JCBpcyB0aGUgcG90ZW50aWFsIG91dGNvbWUgb2YgJFlfMSQgdGhlIGZvbGxvdy11cCBzY29yZSB3aGVuICRYJCBpcyBzZXQgdG8gJHgkIGFuZCAkWV8xXnt4J30kIGlzIHRoZSBwb3RlbnRpYWwgb3V0Y29tZSBvZiAuLi4KCiQkClxtYXRocm17RX0oWV8xXnggLSBZXzBeeCkgLSBcbWF0aHJte0V9KFlfMV57eCd9IC0gWV8wXnt4J30pCiQkIHsjZXEtY2hhbmdlc2NvcmV9CgpXaGVuIHdlIGhhdmUgYm90aCBwb3RlbnRpYWwgb3V0Y29tZXMgZm9yIHRoZSBwb3B1bGF0aW9ucywgdGhpcyBpcyBpZGVudGljYWwgdG8gdGhlIGVxdWF0aW9ucyBhYm92ZSwgYmVjYXVzZSB0aGUgYmFzZWxpbmVzIGNhbmNlbCBvdXQgYWNyb3NzIHRoZSB0d28gbWVhbnMgYmVsb3cuIEluIEBlcS1jaGFuZ2VzY29yZQoKYGBge3J9Cm1lYW4oZGF0YSRZMV90aW1lMiAtIGRhdGEkYmFzZWxpbmUpIC0gbWVhbihkYXRhJFkwX3RpbWUyIC0gZGF0YSRiYXNlbGluZSkKYGBgCgpXaGlsZSB0aGVyZSBpcyBvbmx5IG9uZSBtZWFzdXJlZCBjb3ZhcmlhdGUgaGVyZSAoYGJhc2VsaW5lYCkgYSBjb3ZhcmlhdGUgYmFsYW5jZSBwbG90IHNob3dpbmcgdGhlIHN0YW5kYXJkaXplZCBtZWFuIGRpZmZlcmVuY2VzIGJldHdlZW4gY292YXJpYXRlcyBpbiB0aGUgdHJlYXRlZCBhbmQgdW50cmVhdGVkIGdyb3VwcyAodHJlYXRtZW50IG1lYW4gLSBjb250cm9sIG1lYW4pIHdpbGwgc2hvdyB3aGV0aGVyIHRoZXJlIGlzIGNvdmFyaWF0ZSBpbWJhbGFuY2UgZHVlIHRvIHRoZSBzbWFsbCBzYW1wbGUgc2l6ZS4gSGVyZSwgd2UgaW5jbHVkZSB0aGUgbGF0ZW50IHZhcmlhYmxlIGBzZXZlcml0eWAuIEV2ZW4gdGhvdWdoIHdlIGhhdmUgYW4gUkNULCBib3RoIGNvdmFyaWF0ZXMgYXJlIHNrZXdlZCB0byB0aGUgcmlnaHQuCgpgYGB7cn0KbGlicmFyeShjb2JhbHQpCnVuYWRqY292IDwtIGJhbC50YWIoZ3JvdXAgfiBiYXNlbGluZSArIHNldmVyaXR5LCBkYXRhID0gZGF0YSwgdGhyZXNob2xkcyA9IGMobSA9IC4xKSkKbG92ZS5wbG90KHVuYWRqY292LCBzdGFycyA9ICJzdGQiLCB0aHJlc2hvbGRzID0gYyhtID0gLjEpKQpgYGAKClRoZXJlIGlzIHNvbWUgY29udHJvdmVyc3kgb3ZlciBob3cgdG8gbWVhc3VyZSB0b3RhbCBlZmZlY3Qgb2YgYSB0cmVhdG1lbnQgb24gY2hhbmdlIGZyb20gYmFzZWxpbmUuIElmIHRoZSBiYXNlbGluZSB2YWx1ZXMgb2YgdGhlIHRyZWF0bWVudCBhbmQgY29udHJvbCBncm91cCBhcmUgYmFsYW5jZWQsIHRoZW4gYW4gdW5iaWFzZWQgYXZlcmFnZSB0cmVhdG1lbnQgZWZmZWN0IGlzIGp1c3QgdGhlIG1lYW4gb2YgYHRpbWUyYCBmb3IgdGhvc2UgaW4gdGhlIHRyZWF0bWVudCBncm91cCBtaW51cyB0aGUgbWVhbiBmb3IgdGhvc2UgaW4gdGhlIGNvbnRyb2wgZ3JvdXAuIEluIGFuIFJDVCB3aXRoIHN1ZmZpY2llbnQgc2FtcGxlIHNpemUsIHRoZSBiYXNlbGluZXMgYXJlIGV4cGVjdGVkIHRvIGJlIGJhbGFuY2VkLiBMZXQncyBjaGVjayBvdXIgZGF0YToKCmBgYHtyfQpkYXRhICU+JSBncm91cF9ieShncm91cCkgJT4lIHN1bW1hcml6ZShtZWFuQmFzZWxpbmUgPSBtZWFuKGJhc2VsaW5lKSkKYGBgCgpXaXRoIHRoZSByZWxhdGl2ZWx5IHNtYWxsIHNhbXBsZSBzaXplLCB0aGUgYmFzZWxpbmUgbWVhbnMgYXJlIGNsb3NlLCBidXQgbm90IHBlcmZlY3RseSBiYWxhbmNlZC4gVGhpcyBjb3VsZCBiaWFzIG91ciBlZmZlY3QgZXN0aW1hdGUgaWYgd2Ugc2ltcGx5IHVzZSB0aGUgZGlmZmVyZW5jZSBpbiBtZWFucyBvZiBgdGltZTJgIGJldHdlZW4gVHJlYXRtZW50IGFuZCBDb250cm9sLiBUaGUgbWVhbnMgb2YgYHRpbWUyYCBmb3IgdGhlIENvbnRyb2wgYW5kIFRyZWF0bWVudCBncm91cHMgYXJlOgoKYGBge3J9Cm1lYW5zIDwtIGRhdGEgJT4lIGdyb3VwX2J5KGdyb3VwKSAlPiUgc3VtbWFyaXplKG1lYW4gPSBtZWFuKHRpbWUyKSkKbWVhbnMKYGBgCgpTdWJ0cmFjdGluZyB0aGVzZSB0byBnZXQgdGhlIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdCBpczoKCmBgYHtyfQpmaWx0ZXIobWVhbnMsIGdyb3VwPT0iVHJlYXRtZW50IikkbWVhbiAtIGZpbHRlcihtZWFucywgZ3JvdXA9PSJDb250cm9sIikkbWVhbgpgYGAKCldlIGNhbiBhbHNvIGRvIGEgdC10ZXN0IHRvIHRlc3QgZm9yIGRpZmZlcmVuY2UgaW4gbWVhbnMgZm9yIGB0aW1lMmAgYmV0d2VlbiB0aGUgdHJlYXRtZW50IGFuZCBjb250cm9sIGdyb3VwczoKCmBgYHtyfQp0LnRlc3QodGltZTIgfiBncm91cCwgZGF0YSA9IGRhdGEpCmBgYAoKSGVyZSwgd2Ugc2VlIHRoYXQgdGhlIG1lYW5zIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50bHkgZGlmZmVyZW50LgoKSWYgdGhlIGJhc2VsaW5lIHZhbHVlcyBhcmUgdW5iYWxhbmNlZCwgdGhlIGVmZmVjdCBlc3RpbWF0ZSBvZiB0aGUgZGlmZmVyZW5jZSBpbiBtZWFucyBvZiBgdGltZTJgIHdpbGwgYmUgYmlhc2VkLiBUaGlzIGlzIHdoeSBtYW55IGFuYWx5c3RzIHJlY29tbWVuZCBhbHdheXMgdXNpbmcgYW5hbHlzaXMgb2YgY292YXJpYW5jZSAoQU5DT1ZBKSB3aGVyZSB0aGUgb3V0Y29tZSBpcyByZWdyZXNzZWQgb24gdGhlIHRyZWF0bWVudCBhbmQgYWRqdXN0ZWQgZm9yIHRoZSBiYXNlbGluZSB2YWx1ZToKCmBgYHtyfQpyZXMgPC0gbG0odGltZTIgfiBncm91cCArIGJhc2VsaW5lLCBkYXRhID0gZGF0YSkKc3VtbWFyeShyZXMpCmBgYAoKSGVyZSB3ZSBzZWUgdGhhdCB0aGUgY2F1c2FsIGVmZmVjdCBvZiBgZ3JvdXBgIG9uIGB0aW1lMmAgd2hlbiBhZGp1c3RlZCBmb3IgYmFzZWxpbmUgaXMgYGNvZWYocmVzKVsiZ3JvdXAiXWAuIE5vdGUgYWJvdmUgdGhhdCB0aGUgYmFzZWxpbmUgbWVhbnMgYmV0d2VlbiB0cmVhdG1lbnQgYW5kIGNvbnRyb2wgZGlmZmVyIGJ5IGFyb3VuZCAuNSwgd2hpY2ggZXhwbGFpbnMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgZWZmZWN0IGFzIGNvbXB1dGVkIG9uIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBtZWFuIG9mIGB0aW1lMmAgYmV0d2VlbiBncm91cHMsIHZzLiB0aGUgcmVncmVzc2lvbiByZXN1bHRzLgoKV2UgY2FuIHVzZSBndHN1bW1hcnkgZm9yIGEgbmljZXIgdmlldyBvZiB0aGUgcmVzdWx0czoKCmBgYHtyfQp0YmxfcmVncmVzc2lvbihyZXMsIGVzdGltYXRlX2Z1biA9IHB1cnJyOjpwYXJ0aWFsKHN0eWxlX3JhdGlvLCBkaWdpdHMgPSAyKSkKYGBgCgpJbnRlcnByZXRhdGlvbjogQWZ0ZXIgYWRqdXN0aW5nIGZvciBiYXNlbGluZSBkaWZmZXJlbmNlcywgbWVhbiBgdGltZTJgIGluY3JlYXNlZCBieSBcYHJlc1wkIHVuaXRzIGZvciB0aG9zZSByZWNlaXZpbmcgdGhlIHRyZWF0bWVudCBjb21wYXJlZCB3aXRoIHRob3NlIHdobyBkaWQgbm90IHJlY2VpdmUgdGhlIHRyZWF0bWVudC4gV2UgY2FuIHNlZSB0aGlzIGhlcmU6CgpgYGB7cn0KIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcwpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGxvdGx5KQoKbWVhbl9iYXNlbGluZSA9IG1lYW4oZGF0YSRiYXNlbGluZSkKCiMgQ2FsY3VsYXRlIHByZWRpY3Rpb25zIGF0IG1lYW4gYmFzZWxpbmUgZm9yIGVhY2ggZ3JvdXAKcHJlZDEgPC0gcHJlZGljdChyZXMsIG5ld2RhdGEgPSBkYXRhLmZyYW1lKGdyb3VwID0gbGV2ZWxzKGRhdGEkZ3JvdXApWzFdLCBiYXNlbGluZSA9IG1lYW5fYmFzZWxpbmUpKQpwcmVkMiA8LSBwcmVkaWN0KHJlcywgbmV3ZGF0YSA9IGRhdGEuZnJhbWUoZ3JvdXAgPSBsZXZlbHMoZGF0YSRncm91cClbMl0sIGJhc2VsaW5lID0gbWVhbl9iYXNlbGluZSkpCgojIENyZWF0ZSB0aGUgcGxvdApwIDwtIGdncGxvdChkYXRhLCBhZXMoeCA9IGJhc2VsaW5lLCB5ID0gdGltZTIsIGNvbG9yID0gZ3JvdXApKSArCiAgIyBBZGQgcG9pbnRzCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNiwgc2l6ZSA9IDIpICsKICAKICAjIEFkZCByZWdyZXNzaW9uIGxpbmVzIGZvciBlYWNoIGdyb3VwCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBUUlVFLCBhbHBoYSA9IDAuMikgKwogIAogICMgQWRkIHZlcnRpY2FsIGxpbmUgYXQgbWVhbiBiYXNlbGluZQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW5fYmFzZWxpbmUsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9IDAuOCkgKwogIAogICMgQWRkIHBvaW50cyBmb3IgdGhlIGVuZHBvaW50cyBvZiB0aGUgdmVydGljYWwgbGluZSAodGhlc2Ugd2lsbCBiZSBpbnZpc2libGUpCiAgZ2VvbV9wb2ludChhZXMoeCA9IG1lYW5fYmFzZWxpbmUsIHkgPSBwcmVkMSwgCiAgICAgICAgICAgICAgICB0ZXh0ID0gcGFzdGUoIkdyb3VwOiIsIGxldmVscyhkYXRhJGdyb3VwKVsxXSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+QmFzZWxpbmU6Iiwgcm91bmQobWVhbl9iYXNlbGluZSwgMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICI8YnI+UHJlZGljdGVkIFRpbWUyOiIsIHJvdW5kKHByZWQxLCAyKSkpLAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwgc2l6ZSA9IDMsIGFscGhhID0gMC44LCBpbmhlcml0LmFlcyA9IEZBTFNFKSArCiAgCiAgZ2VvbV9wb2ludChhZXMoeCA9IG1lYW5fYmFzZWxpbmUsIHkgPSBwcmVkMiwKICAgICAgICAgICAgICAgIHRleHQgPSBwYXN0ZSgiR3JvdXA6IiwgbGV2ZWxzKGRhdGEkZ3JvdXApWzJdLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5CYXNlbGluZToiLCByb3VuZChtZWFuX2Jhc2VsaW5lLCAyKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIjxicj5QcmVkaWN0ZWQgVGltZTI6Iiwgcm91bmQocHJlZDIsIDIpKSksCiAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLCBzaXplID0gMywgYWxwaGEgPSAwLjgsIGluaGVyaXQuYWVzID0gRkFMU0UpICsKICAKICAjIEFkZCB0aGUgdmVydGljYWwgbGluZSBzaG93aW5nIGRpZmZlcmVuY2UgYmV0d2VlbiBncm91cHMgYXQgbWVhbiBiYXNlbGluZQogIGdlb21fc2VnbWVudCgKICAgIHggPSBtZWFuX2Jhc2VsaW5lLCAKICAgIHhlbmQgPSBtZWFuX2Jhc2VsaW5lLAogICAgeSA9IHByZWQxLAogICAgeWVuZCA9IHByZWQyLAogICAgY29sb3IgPSAicmVkIiwgCiAgICBzaXplID0gMiwKICAgIGFycm93ID0gYXJyb3coZW5kcyA9ICJib3RoIiwgYW5nbGUgPSA5MCwgbGVuZ3RoID0gdW5pdCgwLjEsICJpbmNoZXMiKSkKICApICsKICAKICAjIEN1c3RvbWl6ZSB0aGUgcGxvdAogIGxhYnMoCiAgICB0aXRsZSA9ICJUaW1lMiB2cyBCYXNlbGluZSBieSBHcm91cCIsCiAgICBzdWJ0aXRsZSA9IHBhc3RlKCJWZXJ0aWNhbCBsaW5lIHNob3dzIGRpZmZlcmVuY2UgYmV0d2VlbiBncm91cHMgYXQgbWVhbiBiYXNlbGluZSAoIiwgCiAgICAgICAgICAgICAgICAgICAgcm91bmQobWVhbl9iYXNlbGluZSwgMiksICIpIiwgc2VwID0gIiIpLAogICAgeCA9ICJCYXNlbGluZSIsCiAgICB5ID0gIlRpbWUyIiwKICAgIGNvbG9yID0gIkdyb3VwIgogICkgKwogIAogICMgQ2xlYW4gdGhlbWUKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQsIGZhY2UgPSAiYm9sZCIpLAogICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTEsIGNvbG9yID0gImdyYXk0MCIpLAogICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIKICApCgojIENvbnZlcnQgdG8gcGxvdGx5IGZvciBpbnRlcmFjdGl2ZSB0b29sdGlwcwpnZ3Bsb3RseShwLCB0b29sdGlwID0gInRleHQiKSAlPiUKICBsYXlvdXQodGl0bGUgPSBsaXN0KHRleHQgPSAiVGltZTIgdnMgQmFzZWxpbmUgYnkgR3JvdXA8YnI+PHN1Yj5WZXJ0aWNhbCBsaW5lIHNob3dzIGRpZmZlcmVuY2UgYmV0d2VlbiBncm91cHMgYXQgbWVhbiBiYXNlbGluZTwvc3ViPiIpKQoKYGBgCgpSZWNhbGwgdGhhdCB0aGUgdHJlYXRtZW50IGVmZmVjdCBpbiB0aGlzIG1vZGVsIGlzIHByb3BvcnRpb25hbCB0byB0aGUgYmFzZWxpbmUgdmFsdWUgYW5kIGluY3JlYXNlZCBieSBgc2V2ZXJpdHlgLCBhIGxhdGVudCB2YXJpYWJsZS4gVGhlIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdCByZXBvcnRlZCBpbiB0aGUgYW5hbHlzaXMgYWJvdmUgaXMgdGhlIGF2ZXJhZ2UgYWNyb3NzIGFsbCBiYXNlbGluZSB2YWx1ZXMgaW4gdGhlIG1vZGVsLgoKIyMgU2ltdWxhdGUgcHJvcG9ydGlvbmFsIHRyZWF0bWVudCBlZmZlY3Qgb24gY2hhbmdlIGZyb20gYmFzZWxpbmUgaW4gYW4gUkNUIHdpdGggY2Vuc29yaW5nCgpGb3IgZGF0YSB3aXRoIGNlbnNvcmluZyBhbmQgcHJvcG9ydGlvbmFsIHRyZWF0bWVudCBlZmZlY3RzOgoKVGhpcyBzaW11bGF0aW9uIGlzIGlkZW50aWNhbCB0byB0aGUgb25lIGFib3ZlLCBidXQgd2l0aCBzb21lIHN1YmplY3RzIGRyb3BwaW5nIG91dCBvZiB0aGUgc3R1ZHkgcHJpb3IgdG8gdGltZTIgYmVpbmcgbWVhc3VyZWQuIENoYW5nZXMgbm90ZWQgaW4gYm9sZC4KClwtIGBzZXZlcml0eWAgaXMgbGF0ZW50IHZhcmlhYmxlIHRoYXQgYWZmZWN0czoKCmEpICBCYXNlbGluZSBzY29yZXMgKHNpY2tlciBwYXRpZW50cyBoYXZlIGxvd2VyIHNjb3JlcykKCmIpICBUcmVhdG1lbnQgZWZmZWN0ICh0cmVhdG1lbnQgd29ya3MgYmV0dGVyIGZvciBzaWNrZXIgcGF0aWVudHMpCgpjKSAgKipDZW5zb3JpbmcgcHJvYmFiaWxpdHkgKHNpY2tlciBwYXRpZW50cyBtb3JlIGxpa2VseSB0byBkcm9wIG91dCkqKgoKXC0gKipUcmVhdG1lbnQgZ3JvdXAgcGFydGljaXBhbnRzIGFyZSBsZXNzIGxpa2VseSB0byBiZSBjZW5zb3JlZCoqCgoqKi0gUGFydGljaXBhbnRzIHdpdGggbGVzcyBpbXByb3ZlbWVudCBhcmUgbW9yZSBsaWtlbHkgdG8gYmUgY2Vuc29yZWQqKgoKVGhlIENhdXNhbCBEQUcgZm9yIHRoaXMgZGF0YSBnZW5lcmF0aW5nIG1vZGVsIGlzIHNob3duIGJlbG93LgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gOH0KbGlicmFyeShkYWdpdHR5KQpsaWJyYXJ5KGdnZGFnKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBDcmVhdGUgdGhlIERBRyB1c2luZyBkYWdpZnkKZGFnMSA8LSBkYWdpZnkoCiAgYmFzZWxpbmUgfiBzZXZlcml0eSwKICB0aW1lMSB+IGJhc2VsaW5lICsgZ3JvdXAgKyBzZXZlcml0eSwKICBjaGFuZ2UxIH4gYmFzZWxpbmUgKyB0aW1lMSwKICBjZW5zb3JlZCB+IGNoYW5nZTEgKyBncm91cCArIHNldmVyaXR5LAogIHRpbWUyIH4gZ3JvdXAgKyB0aW1lMSArIHNldmVyaXR5LAogIGV4cG9zdXJlID0gImdyb3VwIiwKICBvdXRjb21lID0gInRpbWUyIiwKICBjb29yZHMgPSBkYXRhLmZyYW1lKAogICAgbmFtZSA9IGMoImJhc2VsaW5lIiwgInNldmVyaXR5IiwgImdyb3VwIiwgInRpbWUxIiwgImNoYW5nZTEiLCAiY2Vuc29yZWQiLCAidGltZTIiKSwKICAgIHggPSBjKDAsIDAsIDQsIDMsIDEuNSwgOCwgNS41KSwKICAgIHkgPSBjKDEsIDIsIDIsIDEsIDAsIDEsIDEpCiAgKQopCgojIEluZGljYXRlIHRoYXQgYGNlbnNvcmVkYCBpcyBhZGp1c3RlZCBzaW5jZSB3ZSBvbmx5IGhhdmUgZGF0YSBmb3IgcGF0aWVudHMgd2hvIGFyZSBub3QgY2Vuc29yZWQsIGNlbnNvcmVkIGlzIGZvcmNlZCB0byBlcXVhbCAwCgphZGp1c3RlZE5vZGVzKGRhZzEpIDwtICJjZW5zb3JlZCIKcGxvdChkYWcxKQoKI0xpc3QgYWxsIHBhdGhzIGZyb20gdHJlYXRtZW50IGBncm91cGAgdG8gb3V0Y29tZSBgdGltZTJgCiMgYHBhdGhzYCBjdXJyZW50bHkgaWdub3JlcyB0aGUgYWRqdXN0ZWQgbm9kZXMgd2hlbiBkZXRlcm1pbmluZyB3aGljaCBwYXRocyBhcmUgb3Blbiwgc28gd2UgaGF2ZSB0byBzcGVjaWZ5IGFkanVzdGVkIG5vZGVzIGluIHRoZSBjYWxsCnBhdGhzKGRhZzEsIFogPSBhZGp1c3RlZE5vZGVzKGRhZzEpKQoKIyBQcmludCB0aGUgYWRqdXN0bWVudCBzZXRzIChpZiBhbnkpCmFkanVzdG1lbnRTZXRzKGRhZzEpCgoKdGlkeV9kYWcxIDwtIHRpZHlfZGFnaXR0eShkYWcxKSAlPiUgbXV0YXRlKHNoYXBlID0gaWZlbHNlKG5hbWUgPT0gImNlbnNvcmVkIiwgInNxdWFyZSIsICJjaXJjbGUiKSkKCiMgSW5kaWNhdGUgdGhhdCBgY2Vuc29yZWRgIGlzIGFkanVzdGVkIGZvciBpbiB0aGlzIG1vZGVsCnRpZHlfZGFnMSA8LSBhZGp1c3RfZm9yKHRpZHlfZGFnMSwgImNlbnNvcmVkIikKCgojIFBsb3Qgd2l0aCBjdXN0b20gbm9kZSBzaGFwZXMKIyBHZXQgdGhlIHN0YXR1cyAoZXhwb3N1cmUsIG91dGNvbWUpIGluZm9ybWF0aW9uCnN0YXR1c19kYXRhIDwtIGdnZGFnX3N0YXR1cyh0aWR5X2RhZzEpJGRhdGEKc3RhdHVzX2RhdGEgPC0gc3RhdHVzX2RhdGEgJT4lIG11dGF0ZShnc3RhdHVzID0gYXMuY2hhcmFjdGVyKHN0YXR1cyksIGdzdGF0dXMgPSBpZmVsc2UobmFtZSA9PSAiY2Vuc29yZWQiLCAiYWRqdXN0ZWQiLCBhcy5jaGFyYWN0ZXIoc3RhdHVzKSksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdzdGF0dXMgPSBmYWN0b3IoZ3N0YXR1cyxsZXZlbHMgPSBjKGxldmVscyhzdGF0dXMpLCAiYWRqdXN0ZWQiKSkpCgojIFBsb3Qgd2l0aCBjdXN0b20gbm9kZSBzaGFwZXMgd2hpbGUgcHJlc2VydmluZyBzdGF0dXMgY29sb3JpbmcKZ2dwbG90KHN0YXR1c19kYXRhLCBhZXMoeCA9IHgsIHkgPSB5LCB4ZW5kID0geGVuZCwgeWVuZCA9IHllbmQpKSArCiAgZ2VvbV9kYWdfZWRnZXMoYXJyb3dfZGlyZWN0ZWQgPSBncmlkOjphcnJvdyhsZW5ndGggPSB1bml0KDAuNSwgImNtIiksIHR5cGUgPSAiY2xvc2VkIikpICsKICBnZW9tX2RhZ19wb2ludChhZXMoY29sb3IgPSBnc3RhdHVzLCBzaGFwZSA9IHNoYXBlKSwgc2l6ZSA9IDI0KSArCiAgZ2VvbV9kYWdfdGV4dChjb2xvciA9ICJ3aGl0ZSIpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gYyhjaXJjbGUgPSAxNiwgc3F1YXJlID0gMTUpLCBndWlkZSA9ICJub25lIikgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiU3RhdHVzIiwgbmEudmFsdWUgPSAiZ3JleTUwIikgKwogIHRoZW1lX2RhZygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAicmlnaHQiKQoKCgpgYGAKClRoZSBgY2Vuc29yZWRgIHZhcmlhYmxlIGlzIHNob3duIHdpdGggYSBzcXVhcmUgYXJvdW5kIGl0IHRvIGluZGljYXRlIHRoYXQgYnkgYW5hbHl6aW5nIG9ubHkgdW5jZW5zb3JlZCBwYXRpZW50cywgYGNlbnNvcmVkYCBpcyBhZGp1c3RlZCBmb3IgaW4gdGhlIGFuYWx5c2lzLiBTaW5jZSBhbGwgYXJyb3dzIHBvaW50IGludG8gYGNlbnNvcmVkYCwgYW55IHBhdGggZnJvbSB0cmVhdG1lbnQgYGdyb3VwYCB0byBlZmZlY3QgYHRpbWUyYCB0aGF0IGluY2x1ZGVzIGBjZW5zb3JlZGAgd2lsbCB0cmFuc21pdCBzcHVyaW91cyBhc3NvY2lhdGlvbiB0aHJvdWdoIGBjZW5zb3JlZGAgdW5sZXNzIHRoZSBwYXRoIGlzIGJsb2NrZWQgYXQgYW5vdGhlciBwb2ludC4KClRvIHNlZSB0aGlzLCBsZXRzIGxvb2sgYXQgYWxsIHBhdGhzIChjYXVzYWwgYW5kIG5vbi1jYXVzYWwpIGZyb20gYGdyb3VwYCB0byBgdGltZTJgLiBBIGNhdXNhbCBwYXRoIGlzIG9uZSBpbiB3aGljaCBhcnJvd3MgcG9pbnQgZnJvbSB0aGUgdHJlYXRtZW50IGBncm91cGAgdG8gdGhlIG91dGNvbWUgYHRpbWUyYC4gTm9uY2F1c2FsIHBhdGhzIGFyZSB0aG9zZSB3aXRoIGF0IGxlYXN0IG9uZSBhcnJvdyBwb2ludGluZyBiYWNrIHRvd2FyZCB0aGUgdHJlYXRtZW50LgoKYGBge3J9CiMgYHBhdGhzYCBjdXJyZW50bHkgaWdub3JlcyB0aGUgYWRqdXN0ZWQgbm9kZXMgd2hlbiBkZXRlcm1pbmluZyB3aGljaCBwYXRocyBhcmUgb3Blbiwgc28gd2UgaGF2ZSB0byBzcGVjaWZ5IHRoZSBhZGp1c3RlZCBub2RlcyBpbiB0aGUgY2FsbApwYXRocyhkYWcxLCBaID0gYWRqdXN0ZWROb2RlcyhkYWcxKSkKYGBgCgpXZSBzZWUgdGhhdCB0aGVyZSBhcmUgMTggcGF0aHMgZnJvbSBgZ3JvdXBgIHRvIGB0aW1lMmAuIFwkb3BlbiBpcyBhIGJvb2xlYW4gbGlzdCBzaG93aW5nIHdoZXRoZXIgZWFjaCBwYXRoIGlzIG9wZW4gb3IgY2xvc2VkLiBBbGwgb2YgdGhlc2UgcGF0aHMgYXJlIG9wZW4gYW5kIHNpbmNlIG1hbnkgb2YgdGhlc2UgcGF0aHMgYXJlIG5vbmNhdXNhbCAiYmFja2Rvb3IiIHBhdGhzLCBtZWFuaW5nIHRoZSBhcnJvd3MgZG8gbm90IGFsbCBwb2ludCBmcm9tIGBncm91cGAgdG8gYHRpbWUyYCB0aGlzIG1lYW5zIHRoYXQgaWYgd2UgbWVhc3VyZSB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiBgZ3JvdXBgIGFuZCBgdGltZTJgIGluIHRoZSBkYXRhLCB0aGF0IGFzc29jaWF0aW9uIGlzIGxpa2VseSB0byBleGhpYml0IGJpYXMgZnJvbSBjb25mb3VuZGluZy4gRm9yIGV4YW1wbGUsIHBhdGhzIDEgdGhyb3VnaCAxMSBhcmUgYWxsIGJhY2tkb29yIHBhdGhzIHRoYXQgZ28gdGhyb3VnaCBgY2Vuc29yZWRgLiBBIGNvbGxpZGVyIGlzIGEgdmFyaWFibGUgb24gYSBwYXRoIGluIHdoaWNoIGJvdGggYXJyb3dzIHBvaW50IHRvd2FyZCBpdC4gQXMgeW91IGNhbiBzZWUsIGBjZW5zb3JlZGAgaXMgYSBjb2xsaWRlciBvbiBldmVyeSBwYXRoIGl0IGFwcGVhcnMgaW4uIENvbGxpZGVycyBibG9jayBhc3NvY2lhdGlvbiB3aGVuIHRoZXkgYXJlIG5vdCBhZGp1c3RlZCwgYnV0IGFsbG93IGFzc29jaWF0aW9uIHRvIGZsb3cgYW5kIGJpYXMgdGhlIGFuYWx5c2lzIHdoZW4gdGhleSBhcmUgYWRqdXN0ZWQuCgpUbyBwcm9wZXJseSBtZWFzdXJlIHRoZSB0b3RhbCBjYXVzYWwgZWZmZWN0IG9mIGBncm91cGAgb24gYHRpbWUyYCB3ZSBuZWVkIHRvIG1lYXN1cmUgdGhlIGFzc29jaWF0aW9uIHRoYXQgZmxvd3MgdGhyb3VnaCBhbGwgcGF0aHMgdGhhdCBwb2ludCBmcm9tIGBncm91cGAgdG8gYHRpbWUyYC4gTGV0J3MgdXNlIGBwYXRoc2AgdG8gbGlzdCBvbmx5IHRoZSBjYXVzYWwgcGF0aHM6CgpgYGB7cn0KcGF0aHMoZGFnMSwgWiA9IGFkanVzdGVkTm9kZXMoZGFnMSksIGRpcmVjdGVkID0gVFJVRSkKYGBgCgpUaGVyZSBhcmUgdHdvIGNhdXNhbCBwYXRocywgYm90aCBvcGVuLiBUbyBtZWFzdXJlIHRoZSB0b3RhbCBjYXVzYWwgZWZmZWN0LCB3ZSBuZWVkIHRvIGJsb2NrIHRoZSBub24tY2F1c2FsIG9wZW4gcGF0aHMgYnkgYWRqdXN0aW5nIGZvciB2YXJpYWJsZXMsIGlmIHBvc3NpYmxlLiBXZSBjYW4gdXNlIGBnZ2RhZ2AgdG8gbGlzdCB0aGUgYWRqdXN0bWVudCBzZXRzIGZvciB0aGUgdG90YWwgY2F1c2FsIGVmZmVjdCBvZiBgZ3JvdXBgIG9uIGB0aW1lMmAuIFRoaXMgcmV0dXJucyBub3RoaW5nIGJlY2F1c2UgdGhlcmUgaXMgbm8gd2F5IHRvIGFkanVzdCB2YXJpYWJsZXMgdG8gYmxvY2sgdGhlIG9wZW4gcGF0aCBjYXVzZWQgdGhyb3VnaCB0aGUgY29sbGlkZXIgYGNlbnNvcmVkLmAKCmBgYHtyfQphZGp1c3RtZW50U2V0cyhkYWcxLCBlZmZlY3QgPSAidG90YWwiKQoKYGBgCgpXZSB3aWxsIHVzZSBJUCBXZWlnaHRpbmcgZm9yIGNlbnNvcmluZyBiZWxvdywgYnV0IGJlZm9yZSB0aGF0LCBsZXRzIGFuYWx5emUgdGhpcyBkYXRhc2V0IGFzIGlmIHRoZXJlIHdlcmUgbm8gbG9zcyB0byBmb2xsb3ctdXAuCgojIFNhbWUgbW9kZWwgd2l0aCBubyBsb3NzIHRvIGZvbGxvdy11cAoKVGhlIHNhbWUgZGF0YSBnZW5lcmF0aW5nIG1vZGVsIGFib3ZlIGluY2x1ZGVzIHRpbWUyX2FsbCB3aXRoIG91dGNvbWUgdmFsdWVzIGZvciBhbGwgcGF0aWVudHMuIFdpdGhvdXQgQ2Vuc29yaW5nIHRoZSBDYXVzYWwgREFHIGZvciB0aW1lMl9hbGwgbG9va3MgbGlrZSB0aGlzOgoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQpsaWJyYXJ5KGRhZ2l0dHkpCmxpYnJhcnkoZ2dkYWcpCmxpYnJhcnkodGlkeXZlcnNlKQojIENyZWF0ZSB0aGUgREFHIHVzaW5nIGRhZ2l0aWZ5CmRhZ19ub2xvc3MgPC0gZGFnaWZ5KAogIGJhc2VsaW5lIH4gc2V2ZXJpdHksCiAgdGltZTEgfiBiYXNlbGluZSArIGdyb3VwICsgc2V2ZXJpdHksCiAgY2hhbmdlMSB+IGJhc2VsaW5lICsgdGltZTEsCiAgdGltZTJfYWxsIH4gZ3JvdXAgKyB0aW1lMSArIHNldmVyaXR5LAogIGV4cG9zdXJlID0gImdyb3VwIiwKICBvdXRjb21lID0gInRpbWUyX2FsbCIsCiAgY29vcmRzID0gZGF0YS5mcmFtZSgKICAgIG5hbWUgPSBjKCJiYXNlbGluZSIsICJzZXZlcml0eSIsICJncm91cCIsICJ0aW1lMSIsICJjaGFuZ2UxIiwgImNlbnNvcmVkIiwgInRpbWUyX2FsbCIpLAogICAgeCA9IGMoMCwgMCwgNCwgMywgMS41LCA4LCA1LjUpLAogICAgeSA9IGMoMSwgMiwgMiwgMSwgMCwgMSwgMSkKICApCikKCnRpZHlfZGFnX25vbG9zcyA8LSB0aWR5X2RhZ2l0dHkoZGFnX25vbG9zcykKCiMgUGxvdCB3aXRoIGN1c3RvbSBub2RlIHNoYXBlcwojIEdldCB0aGUgc3RhdHVzIChleHBvc3VyZSwgb3V0Y29tZSkgaW5mb3JtYXRpb24Kc3RhdHVzX2RhdGEgPC0gZ2dkYWdfc3RhdHVzKHRpZHlfZGFnX25vbG9zcykkZGF0YQpzdGF0dXNfZGF0YSA8LSBzdGF0dXNfZGF0YSAlPiUgbXV0YXRlKGdzdGF0dXMgPSBhcy5jaGFyYWN0ZXIoc3RhdHVzKSwgZ3N0YXR1cyA9IGlmZWxzZShuYW1lID09ICJjZW5zb3JlZCIsICJhZGp1c3RlZCIsIGFzLmNoYXJhY3RlcihzdGF0dXMpKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3N0YXR1cyA9IGZhY3Rvcihnc3RhdHVzLGxldmVscyA9IGMobGV2ZWxzKHN0YXR1cyksICJhZGp1c3RlZCIpKSkKCiMgUGxvdCB3aXRoIGN1c3RvbSBub2RlIHNoYXBlcyB3aGlsZSBwcmVzZXJ2aW5nIHN0YXR1cyBjb2xvcmluZwpnZ3Bsb3Qoc3RhdHVzX2RhdGEsIGFlcyh4ID0geCwgeSA9IHksIHhlbmQgPSB4ZW5kLCB5ZW5kID0geWVuZCkpICsKICBnZW9tX2RhZ19lZGdlcyhhcnJvd19kaXJlY3RlZCA9IGdyaWQ6OmFycm93KGxlbmd0aCA9IHVuaXQoMC41LCAiY20iKSwgdHlwZSA9ICJjbG9zZWQiKSkgKwogIGdlb21fZGFnX3BvaW50KGFlcyhjb2xvciA9IGdzdGF0dXMpLCBzaXplID0gMjQpICsKICBnZW9tX2RhZ190ZXh0KGNvbG9yID0gIndoaXRlIikgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBjKGNpcmNsZSA9IDE2LCBzcXVhcmUgPSAxNSksIGd1aWRlID0gIm5vbmUiKSArCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobmFtZSA9ICJTdGF0dXMiLCBuYS52YWx1ZSA9ICJncmV5NTAiKSArCiAgdGhlbWVfZGFnKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpCgoKCmBgYAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KIyBMb2FkIHJlcXVpcmVkIGxpYnJhcmllcwpsaWJyYXJ5KGRhZ2l0dHkpCmxpYnJhcnkoZ2dkYWcpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKCiMgRGVmaW5lIHRoZSBEQUcKZGFnMSA8LSBkYWdpdHR5KCdkYWcgewpiYj0iMCwwLDEsMSIKYmFzZWxpbmUgW3Bvcz0iMC4zNDgsMC41NDIiXQpjZW5zb3JlZCBbYWRqdXN0ZWQscG9zPSIwLjkzMSwwLjQ5OSJdCmNoYW5nZTEgW3Bvcz0iMC40NzcsMC42NDEiXQpncm91cCBbZXhwb3N1cmUscG9zPSIwLjM0MywwLjM2OCJdCnNldmVyaXR5IFtwb3M9IjAuMzQ4LDAuNDQ5Il0KdGltZTEgW3Bvcz0iMC40ODYsMC41NDAiXQp0aW1lMiBbb3V0Y29tZSxwb3M9IjAuNjA1LDAuNTM5Il0KYmFzZWxpbmUgLT4gY2hhbmdlMQpiYXNlbGluZSAtPiB0aW1lMQpjaGFuZ2UxIC0+IGNlbnNvcmVkCmdyb3VwIC0+IGNlbnNvcmVkCmdyb3VwIC0+IHRpbWUxCmdyb3VwIC0+IHRpbWUyCnNldmVyaXR5IC0+IGJhc2VsaW5lCnNldmVyaXR5IC0+IGNlbnNvcmVkCnNldmVyaXR5IC0+IHRpbWUxCnNldmVyaXR5IC0+IHRpbWUyCnRpbWUxIC0+IGNoYW5nZTEKdGltZTEgLT4gdGltZTIKfScKKQoKcGxvdChkYWcxKQoKCgpgYGAKCiMjIEdlbmVyYXRlIERhdGEgQWNjb3JkaW5nIHRvIHRoZSBUd28gQ2F1c2FsIERBR3MgQWJvdmUKCmBgYHtyfQojIEluc3RhbGwgYW5kIGxvYWQgcmVxdWlyZWQgcGFja2FnZXMKIyBVbmNvbW1lbnQgdG8gaW5zdGFsbCBwYWNrYWdlcyBpZiBuZWVkZWQKIyBpbnN0YWxsLnBhY2thZ2VzKGMoImxhdmFhbiIsICJzZW1QbG90IiwgInRpZHl2ZXJzZSIsICJwc3ljaCIpKQoKbGlicmFyeShsYXZhYW4pCmxpYnJhcnkoc2VtUGxvdCkKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkocHN5Y2gpCgojIFNldCBzZWVkIGZvciByZXByb2R1Y2liaWxpdHkKc2V0LnNlZWQoMTIzKQoKIyBTaW11bGF0ZSBkYXRhIGZvciBkZW1vbnN0cmF0aW9uCiMgMTUwIHBhcnRpY2lwYW50cywgMiBncm91cHMgKHRyZWF0bWVudCBhbmQgY29udHJvbCkKbiA8LSAxNTAKZ3JvdXAgPC0gcmVwKGMoMCwgMSksIGVhY2ggPSBuLzIpICAjIDAgPSBjb250cm9sLCAxID0gdHJlYXRtZW50CgojIEdlbmVyYXRlIGJhc2VsaW5lIHNjb3JlcyAoc2ltaWxhciBmb3IgYm90aCBncm91cHMpCmJhc2VsaW5lIDwtIHJub3JtKG4sIG1lYW4gPSA1MCwgc2QgPSAxMCkKCiMgQWRkIGEgbGF0ZW50ICJzZXZlcml0eSIgdmFyaWFibGUgdGhhdCBhZmZlY3RzIGJvdGggYmFzZWxpbmUgYW5kIGNlbnNvcmluZwojIFRoaXMgY3JlYXRlcyB0aGUgbWlzc2luZyBub3QgYXQgcmFuZG9tIChNTkFSKSBzY2VuYXJpbwpzZXZlcml0eSA8LSBybm9ybShuLCBtZWFuID0gMCwgc2QgPSAxKQoKIyBBZGp1c3QgYmFzZWxpbmUgYnkgc2V2ZXJpdHkgKHNpY2tlciBwYXRpZW50cyBoYXZlIGxvd2VyIGJhc2VsaW5lIHNjb3JlcykKYmFzZWxpbmUgPC0gYmFzZWxpbmUgLSA1ICogc2V2ZXJpdHkKCiMgR2VuZXJhdGUgZm9sbG93LXVwIHNjb3JlcyB3aXRoIHByb3BvcnRpb25hbCBlZmZlY3RzCnByb3BfZWZmZWN0MSA8LSAwLjIwICAjIDIwJSBpbXByb3ZlbWVudCBhdCB0aW1lMSBmb3IgdHJlYXRtZW50IGdyb3VwCnByb3BfZWZmZWN0MiA8LSAwLjE1ICAjIEFkZGl0aW9uYWwgMTUlIGltcHJvdmVtZW50IGF0IHRpbWUyIGZvciB0cmVhdG1lbnQgZ3JvdXAKCiMgQ2FsY3VsYXRlIHRpbWUxIHZhbHVlcyAtIHRydWUgZWZmZWN0IGlzIHByb3BvcnRpb25hbCB0byBiYXNlbGluZQojIFRoZSB0cmVhdG1lbnQgYWxzbyBoYXMgYSBzdHJvbmdlciBlZmZlY3QgZm9yIHBhdGllbnRzIHdpdGggaGlnaGVyIHNldmVyaXR5CnRpbWUxIDwtIHZlY3RvcigibnVtZXJpYyIsIG4pCmZvciAoaSBpbiAxOm4pIHsKICBpZiAoZ3JvdXBbaV0gPT0gMSkgewogICAgIyBUcmVhdG1lbnQgZ3JvdXA6IGVmZmVjdCBpcyBwcm9wb3J0aW9uYWwgdG8gYmFzZWxpbmUgYW5kIGluY3JlYXNlZCBieSBzZXZlcml0eQogICAgZWZmZWN0X21vZGlmaWVyIDwtIDEgKyBwcm9wX2VmZmVjdDEgKyAwLjEgKiBzZXZlcml0eVtpXQogICAgdGltZTFbaV0gPC0gYmFzZWxpbmVbaV0gKiBlZmZlY3RfbW9kaWZpZXIgKyBybm9ybSgxLCBtZWFuID0gMCwgc2QgPSAzKQogIH0gZWxzZSB7CiAgICAjIENvbnRyb2wgZ3JvdXA6IHNtYWxsIHJhbmRvbSBjaGFuZ2UKICAgIHRpbWUxW2ldIDwtIGJhc2VsaW5lW2ldICsgcm5vcm0oMSwgbWVhbiA9IDAsIHNkID0gMykKICB9Cn0KCiMgQ2FsY3VsYXRlIHRpbWUyIHZhbHVlcwp0aW1lMiA8LSB2ZWN0b3IoIm51bWVyaWMiLCBuKQpmb3IgKGkgaW4gMTpuKSB7CiAgaWYgKGdyb3VwW2ldID09IDEpIHsKICAgICMgVHJlYXRtZW50IGdyb3VwOiBhZGRpdGlvbmFsIGVmZmVjdCBwcm9wb3J0aW9uYWwgdG8gdGltZTEKICAgIGVmZmVjdF9tb2RpZmllciA8LSAxICsgcHJvcF9lZmZlY3QyICsgMC4wNSAqIHNldmVyaXR5W2ldCiAgICB0aW1lMltpXSA8LSB0aW1lMVtpXSAqIGVmZmVjdF9tb2RpZmllciArIHJub3JtKDEsIG1lYW4gPSAwLCBzZCA9IDMpCiAgfSBlbHNlIHsKICAgICMgQ29udHJvbCBncm91cDogc21hbGwgcmFuZG9tIGNoYW5nZQogICAgdGltZTJbaV0gPC0gdGltZTFbaV0gKyBybm9ybSgxLCBtZWFuID0gMCwgc2QgPSAzKQogIH0KfQoKIyBTYXZlIHRpbWUyIHZhbHVlcyBwcmlvciB0byBjZW5zb3JpbmcgdG8gY3JlYXRlIGEgbW9kZWwgd2l0aG91dCBjZW5zb3JpbmcKdGltZTJfYWxsIDwtIHRpbWUyCgojIEdlbmVyYXRlIGNlbnNvcmluZyBpbmRpY2F0b3JzCiMgSGlnaGVyIHByb2JhYmlsaXR5IG9mIGNlbnNvcmluZyAoZHJvcG91dCkgZm9yOgojIDEuIFNpY2tlciBwYXRpZW50cyAoaGlnaGVyIHNldmVyaXR5KQojIDIuIENvbnRyb2wgZ3JvdXAgcGFydGljaXBhbnRzICh0cmVhdG1lbnQgaGFzIGJlbmVmaXQgdGhhdCBrZWVwcyBwZW9wbGUgZW5nYWdlZCkKIyAzLiBUaG9zZSB3aXRoIHNtYWxsZXIgaW1wcm92ZW1lbnRzIGZyb20gYmFzZWxpbmUgdG8gdGltZTEKCiMgQ2FsY3VsYXRlIGNoYW5nZSBmcm9tIGJhc2VsaW5lIHRvIHRpbWUxCmNoYW5nZV90aW1lMSA8LSB0aW1lMSAtIGJhc2VsaW5lCgojIENhbGN1bGF0ZSBjZW5zb3JpbmcgcHJvYmFiaWxpdGllcwojIExvZ2l0IG1vZGVsIGZvciBjZW5zb3JpbmcKbG9naXRfY2Vuc29yaW5nIDwtIC0yICsgCiAgICAgICAgICAgICAgICAgICAxLjUgKiBzZXZlcml0eSArICAgICAgICAgICAgICAgICMgU2lja2VyIHBhdGllbnRzIG1vcmUgbGlrZWx5IHRvIGRyb3Agb3V0CiAgICAgICAgICAgICAgICAgICAtMC41ICogZ3JvdXAgKyAgICAgICAgICAgICAgICAgIyBUcmVhdG1lbnQgZ3JvdXAgbGVzcyBsaWtlbHkgdG8gZHJvcCBvdXQKICAgICAgICAgICAgICAgICAgIC0wLjA1ICogY2hhbmdlX3RpbWUxICAgICAgICAgICAjIFRob3NlIGltcHJvdmluZyBsZXNzIGxpa2VseSB0byBkcm9wIG91dAoKIyBDb252ZXJ0IHRvIHByb2JhYmlsaXR5CnBfY2Vuc29yZWQgPC0gMSAvICgxICsgZXhwKC1sb2dpdF9jZW5zb3JpbmcpKQoKIyBHZW5lcmF0ZSBjZW5zb3JpbmcgaW5kaWNhdG9yICgxID0gY2Vuc29yZWQvZHJvcHBlZCBvdXQsIDAgPSBvYnNlcnZlZCkKY2Vuc29yZWQgPC0gcmJpbm9tKG4sIDEsIHBfY2Vuc29yZWQpCgojIEFwcGx5IGNlbnNvcmluZyB0byB0aGUgZGF0YSAoc2V0IHZhbHVlcyB0byBOQSkKdGltZTJbY2Vuc29yZWQgPT0gMV0gPC0gTkEKCiMgQ3JlYXRlIGRhdGEgZnJhbWUKZGF0YSA8LSBkYXRhLmZyYW1lKAogIGlkID0gMTpuLAogIGdyb3VwID0gZmFjdG9yKGdyb3VwLCBsYWJlbHMgPSBjKCJDb250cm9sIiwgIlRyZWF0bWVudCIpKSwKICBiYXNlbGluZSA9IGJhc2VsaW5lLAogIHRpbWUxID0gdGltZTEsCiAgdGltZTIgPSB0aW1lMiwKICB0aW1lMl9hbGwgPSB0aW1lMl9hbGwsCiAgc2V2ZXJpdHkgPSBzZXZlcml0eSwgICMgSW5jbHVkZSB0aGUgbGF0ZW50IHNldmVyaXR5IHZhcmlhYmxlCiAgY2Vuc29yZWQgPSBjZW5zb3JlZCAgICAjIEluY2x1ZGUgdGhlIGNlbnNvcmluZyBpbmRpY2F0b3IKKQoKIyBDcmVhdGUgY2hhbmdlIHNjb3JlcyBmb3IgdGhlIGZ1bGwgZGF0YXNldApkYXRhJGNoYW5nZTEgPC0gZGF0YSR0aW1lMSAtIGRhdGEkYmFzZWxpbmUKZGF0YSRjaGFuZ2UyIDwtIGRhdGEkdGltZTIgLSBkYXRhJGJhc2VsaW5lCmRhdGEKYGBgCgojIEFuYWx5emUgSWRlYWwgRGF0YXNldCB3aXRoIG5vIExvc3MgdG8gRm9sbG93LVVwCgpMZXQncyBhbmFseXplIHRoaXMgZGF0YXNldCB1c2luZyB0aGUgZGlmZmVyZW5jZSBpbiBncm91cCBtZWFuczoKCmBgYHtyfQpncm91cF9tZWFucyA8LSBkYXRhICU+JSBncm91cF9ieShncm91cCkgJT4lIHN1bW1hcml6ZShtZWFuKHRpbWUyX2FsbCkpCmdyb3VwX21lYW5zCmBgYAoKVGhlIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdCB3aGVuIHRoZXJlIGlzIG5vIGxvc3MgdG8gZm9sbG93LXVwIGlzIGFuZCB3ZSBkbyBub3QgYWRqdXN0IGZvciBiYXNlbGluZSwgd2Ugc2VlOgoKYGBge3J9Cmdyb3VwX21lYW5zICU+JSBkZWZyYW1lICU+JSAuW1siVHJlYXRtZW50Il1dIC0gZ3JvdXBfbWVhbnMgJT4lIGRlZnJhbWUgJT4lIC5bWyJDb250cm9sIl1dCmBgYAoKVXNpbmcgQU5DT1ZBIHdlIGdldDoKCmBgYHtyfQppZGVhbCA8LSBnbG0odGltZTJfYWxsIH4gYmFzZWxpbmUgKyBncm91cCwgZGF0YSA9IGRhdGEpCnN1bW1hcnkoaWRlYWwpCmBgYAoKVGhlIGVmZmVjdCBlc3RpbWF0ZSBoZXJlIGlzIDE3LjY1LCB3aGljaCBpcyBoaWdoZXIgdGhhbiB0aGUgZGlmZmVyZW5jZSBpbiBncm91cCBtZWFucyBvZiAxNi4zOC4KCldoZW4gdGhlcmUgaXMgbG9zcyB0byBmb2xsb3ctdXAsIHRoZSBuw6RpdmUgYXBwcm9hY2ggdG8gZXN0aW1hdGluZyB0aGUgYXZlcmFnZSB0cmVhdG1lbnQgZWZmZWN0IGlzIHRvIGlnbm9yZSBwYXRpZW50cyB3aG8gd2VyZSBjZW5zb3JlZCwgd2hpY2ggbWVhbnMgcmVtb3ZpbmcgdGhlbSBmcm9tIHRoZSBkYXRhLiBUaGUgYXZlcmFnZSB0cmVhdG1lbnQgZWZmZWN0IGlzIHRoZW4gdGhlIG1lYW4gb3V0Y29tZSAodGltZTIpIG9mIHRoZSB0cmVhdGVkIGdyb3VwIG1pbnVzIHRoZSBtZWFuIG91dGNvbWUgKHRpbWUyKSBvZiB0aGUgdW50cmVhdGVkIGluIHRoZSByZW1haW5pbmcgcGF0aWVudHMuIEhlcmUgd2UgcmV0dXJuIHRvIHVzaW5nIHRpbWUyLCB3aGljaCBoYXMgbWlzc2luZyB2YWx1ZXMgZm9yIGNlbnNvcmVkIHBhdGllbnRzLgoKYGBge3J9Cmdyb3VwX21lYW5zX3VuY2Vuc29yZWQgPC0gZGF0YSAlPiUgZmlsdGVyKGNlbnNvcmVkID09IDApICU+JSBncm91cF9ieShncm91cCkgJT4lIHN1bW1hcml6ZShtZWFuKHRpbWUyKSkKZ3JvdXBfbWVhbnNfdW5jZW5zb3JlZApgYGAKCklnbm9yaW5nIGNlbnNvcmVkIHBhdGllbnRzIHVuZGVyZXN0aW1hdGVzIHRoZSB0cmVhdG1lbnQgZWZmZWN0LCBiZWNhdXNlIHBhdGllbnRzIHdobyBoYXZlIGhpZ2hlciBiYXNlbGluZSB2YWx1ZXMgaGF2ZSBoaWdoZXIgdHJlYXRtZW50IGVmZmVjdHMsIGJ1dCBhcmUgYWxzbyBtb3JlIGxpa2VseSB0byBkcm9wIG91dCAoYmUgbG9zdCB0byBmb2xsb3ctdXApOgoKYGBge3J9Cmdyb3VwX21lYW5zX3VuY2Vuc29yZWQgJT4lIGRlZnJhbWUgJT4lIC5bWyJUcmVhdG1lbnQiXV0gLSBncm91cF9tZWFuc191bmNlbnNvcmVkICU+JSBkZWZyYW1lICU+JSAuW1siQ29udHJvbCJdXQpgYGAKCldlIGNhbiBhbHNvIHVzZSBsb2dpc3RpYyByZWdyZXNzaW9uIHRvIGRvIHRoZSBzYW1lIG7DpGl2ZSBhbmFseXNpcywgdXNpbmcgdGhlIEFOQ09WQSBhcHByb2FjaCBvZiBhZGp1c3RpbmcgZm9yIGJhc2VsaW5lLgoKYGBge3J9Cm1vZGVsX25haXZlIDwtIGxtKHRpbWUyIH4gZ3JvdXAgKyBiYXNlbGluZSwKICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzdWJzZXQoZGF0YSwgIWlzLm5hKHRpbWUyKSkpCm1vZGVsX25haXZlCmBgYAoKVGhpcyByZXN1bHQgaXMgY2xvc2VyIHRvIHRoZSB0cnVlIHJlc3VsdCBvZiAxNy42NQoKT25lIGFwcHJvcHJpYXRlIHdheSB0byBhbmFseXplIGFuIFJDVCB3aXRoIGNlbnNvcmVkIHBhdGllbnRzIGlzIGJ5IHVzaW5nIElQIChJbnZlcnNlIFByb2JhYmlsaXR5KSB3ZWlnaHRpbmcgdG8gYWRqdXN0IHRoZSBkYXRhIGZvciB0aGUgcHJvYmFiaWxpdHkgb2YgcmVtYWluaW5nIGluIHRoZSBzdHVkeS4gVGhpcyBjcmVhdGVzIGEgcHNldWRvLXBvcHVsYXRpb24gd2hlcmUgdGhlIG91dGNvbWUgZm9yIHBhdGllbnRzIHdobyB3ZXJlIHVuY2Vuc29yZWQgZ2V0cyB3ZWlnaHRlZCBieSB0aGVpciBpbnZlcnNlIHByb2JhYmlsaXR5IG9mIHN0YXlpbmcgaW4gdGhlIHN0dWR5IGFzIGNhbGN1bGF0ZWQgdXNpbmcgYmFzZWxpbmUgY292YXJpYXRlcy4KClRvIGRvIHRoaXMsIHdlIGZpcnN0IHVzZSBsb2dpc3RpYyByZWdyZXNzaW9uIHRvIGNyZWF0ZSBhIG1vZGVsIHRoYXQgcHJlZGljdHMgd2hldGhlciBhIHBhdGllbnQgd2lsbCBiZSBjZW5zb3JlZCBiYXNlZCBvbiBncm91cCAodGhlIHRyZWF0bWVudCkgYW5kIGJhc2VsaW5lIEhCQTFDLgoKYGBge3J9CiMgTW9kZWwgZm9yIGNlbnNvcmluZyBwcm9jZXNzIC0gbW9kZWxpbmcgZmFjdG9ycyBhZmZlY3RpbmcgZHJvcG91dAptb2RlbF9jZW5zb3JpbmcgPC0gZ2xtKGNlbnNvcmVkIH4gZ3JvdXAgKyBiYXNlbGluZSArIHRpbWUxLCAKICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXRhLAogICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQoKc3VtbWFyeShtb2RlbF9jZW5zb3JpbmcpCmBgYAoKVGhlIG1vZGVsIGZvciBjZW5zb3JpbmcgaXMgY29uc2lzdGVudCB3aXRoIHRoZSBkYXRhIGdlbmVyYXRpbmcgcHJvY2VzcyBpbiB3aGljaCB0aGVyZSBpcyBhIEhpZ2hlciBwcm9iYWJpbGl0eSBvZiBjZW5zb3JpbmcgKGRyb3BvdXQpIGZvcjoKCjEuICBTaWNrZXIgcGF0aWVudHMgKGhpZ2hlciBzZXZlcml0eSkKCjIuICBDb250cm9sIGdyb3VwIHBhcnRpY2lwYW50cyAodHJlYXRtZW50IGhhcyBiZW5lZml0IHRoYXQga2VlcHMgcGVvcGxlIGVuZ2FnZWQpCgozLiAgVGhvc2Ugd2l0aCBzbWFsbGVyIGltcHJvdmVtZW50cyBmcm9tIGJhc2VsaW5lIHRvIHRpbWUxCgpOb3cgd2UgY2FuIHByZWRpY3QgdGhlIHByb2JhYmlsaXR5IG9mIHJlbWFpbmluZyBpbiB0aGUgc3R1ZHkgKG5vdCBiZWluZyBjZW5zb3JlZCkgYnkgdXNpbmcgdGhlIHJlZ3Jlc3Npb24gcmVzdWx0cyBvbiBlYWNoIHBhdGllbnQgaW4gdGhlIHN0dWR5IGFuZCBjYWxjdWxhdGluZyAxIC0gcChjZW5zb3JlZCkgTG9vayBhdCB0aGUgcHJvYmFiaWxpdHkgb2YgcmVtYWluaW5nIGluIHRoZSBzdHVkeSB0byBjb252aW5jZSB5b3Vyc2VsZiB0aGF0IHRoZSBtb2RlbCBpcyBwcm9kdWNpbmcgYXBwcm9wcmlhdGUgcmVzdWx0cy4gRm9yIGV4YW1wbGUsIHBhdGllbnRzIGluIHRoZSB0cmVhdG1lbnQgZ3JvdXAgYXJlIG1vcmUgbGlrZWx5IHRvIHJlbWFpbiBpbiB0aGUgc3R1ZHksIGFzIGV4cGVjdGVkLgoKYGBge3J9CmRhdGEkcF9vYnNlcnZlZCA8LSAxIC0gcHJlZGljdChtb2RlbF9jZW5zb3JpbmcsIHR5cGUgPSAicmVzcG9uc2UiKQpkYXRhCmBgYAoKV2UgY2FuIGNvbXBhcmUgY292YXJpYXRlcyBiYXNlZCBvbiB0cmVhdG1lbnQgZ3JvdXAuIE5vdGljZSB0aGF0IHRob3NlIGluIHRoZSBjb250cm9sIGdyb3VwIGFyZSBtb3JlIGxpa2VseSB0byBiZSBjZW5zb3JlZCBhbmQgdGhlcmVmb3JlIGhhdmUgYSBsb3dlciBwcm9iYWJpbGl0eSBvZiBiZWluZyBvYnNlcnZlZCBhdCB0aW1lMjoKCmBgYHtyfQp0YWJsZTIgPC0gdGJsX3N1bW1hcnkoZGF0YSwgYnkgPSBncm91cCkgfD4gYWRkX3AoKSB8PiBib2xkX2xhYmVscygpCnRhYmxlMgpgYGAKCk5vdyB3ZSBhZGQgdGhlIElQIHdlaWdodHMgdG8gdGhlIGRhdGEKCmBgYHtyfQojIENyZWF0ZSB3ZWlnaHRzIGFzIGludmVyc2Ugb2YgcHJvYmFiaWxpdHkgb2YgYmVpbmcgb2JzZXJ2ZWQKZGF0YSRpcHcgPC0gMSAvIGRhdGEkcF9vYnNlcnZlZApgYGAKCkZpbmFsbHksIHdlIGNhbiBkbyBhIHNlY29uZCByZWdyZXNzaW9uIHdpdGggdGhlIElQIHdlaWdodHMgdG8gZXN0aW1hdGUgdGhlIGF2ZXJhZ2UgdHJlYXRtZW50IGVmZmVjdDoKCmBgYHtyfQoKIyBXZWlnaHRlZCBhbmFseXNpcwptb2RlbF9pcHcgPC0gZ2xtKHRpbWUyIH4gZ3JvdXAgKyBiYXNlbGluZSwgCiAgICAgICAgICAgICAgICBkYXRhID0gc3Vic2V0KGRhdGEsICFpcy5uYSh0aW1lMikpLAogICAgICAgICAgICAgICAgd2VpZ2h0cyA9IGlwdykKCnN1bW1hcnkobW9kZWxfaXB3KQpgYGAKCkZJbmFsbHkgd2UgdXNlIHRoaXMgbW9kZWwgYW5kIEctQ29tcHV0YXRpb24gdG8gY3JlYXRlIHRoZSBjb3VudGVyZmFjdHVhbHMgKHRoZSBvdXRjb21lcyB3aGVuIHRyZWF0ZWQgYW5kIHVudHJlYWRlZCkgZm9yIGFsbCBwYXRpZW50cy4KClRvIGRvIHRoaXMsIHdlIGNvcHkgdGhlIGRhdGFzZXQsIHNldCBhbGwgcGF0aWVudHMgdG8gdGhlIFRyZWF0bWVudCBncm91cCwgdGhlbiB1c2UgdGhlIG1vZGVsIHRvIHByZWRpY3QgdGltZTJfMS4gVGhlbiB3ZSBkbyB0aGUgc2FtZSBmb3IgdGhlIGNvbnRyb2wgdG8gY3JlYXRlIHRpbWUyXzAuIFRoZSBkaWZmZXJlbmNlIGluIG1lYW5zIGdpdmUgdXMgdGhlIGVmZmVjdC4KCmBgYHtyfQojIENvcHkgZGF0YXNldApkYXRhcCA8LSBkYXRhCgojIFNldCBhbGwgcGF0aWVudHMgdG8gVHJlYXRtZW50CmRhdGFwIDwtIGRhdGFwICU+JSBtdXRhdGUoZ3JvdXAgPSAiVHJlYXRtZW50IikKCiMgUHJlZGljdCBhbmQgc3RvcmUgdGltZTJfMQp0aW1lMl8xIDwtIHByZWRpY3QobW9kZWxfaXB3LCBuZXdkYXRhID0gZGF0YXAsIHR5cGUgPSAicmVzcG9uc2UiKQoKIyBTZXQgYWxsIHBhdGllbnRzIHRvIENvbnRyb2wKZGF0YXAgPC0gZGF0YXAgJT4lIG11dGF0ZShncm91cCA9ICJDb250cm9sIikKCiMgUHJlZGljdCB0aW1lMl8wIChmb3IgY29udHJvbCkKdGltZTJfMCA8LSBwcmVkaWN0KG1vZGVsX2lwdywgbmV3ZGF0YSA9IGRhdGFwLCB0eXBlID0gInJlc3BvbnNlIikKCiMgRmluZCB0aGUgZGlmZmVyZW5jZSBpbiBtZWFucwptZWFuKHRpbWUyXzEpIC0gbWVhbih0aW1lMl8wKQpgYGAKClRoaXMgZXN0aW1hbmQgZG9lcyBub3QgYWRqdXN0IHRoZSBvdXRjb21lIGZvciBiYXNlbGluZSBzY29yZXMuCgojIEFuYWx5emluZyBDZW5zb3JlZCBEYXRhc2V0CgpgYGB7cn0KIyBDcmVhdGUgaW50ZXJhY3Rpb24gdGVybSBmb3IgdGhlIG1vZGVsIC0gZG8gdGhpcyBCRUZPUkUgY3JlYXRpbmcgdGhlIHVuY2Vuc29yZWQgc3Vic2V0CmRhdGEkZ3JvdXBfbnVtZXJpYyA8LSBhcy5udW1lcmljKGRhdGEkZ3JvdXApIC0gMSAgIyBDb252ZXJ0IHRvIDAvMQpkYXRhJGJhc2VsaW5lX2J5X2dyb3VwIDwtIGRhdGEkYmFzZWxpbmUgKiBkYXRhJGdyb3VwX251bWVyaWMKCiMgQ3JlYXRlIHVuY2Vuc29yZWQgc3Vic2V0IC0gQUZURVIgY3JlYXRpbmcgYWxsIGRlcml2ZWQgdmFyaWFibGVzCmRhdGFfdW5jZW5zb3JlZCA8LSBzdWJzZXQoZGF0YSwgY2Vuc29yZWQgPT0gMCkKcHJpbnQocGFzdGUoIk51bWJlciBvZiBjZW5zb3JlZCBvYnNlcnZhdGlvbnM6Iiwgc3VtKGRhdGEkY2Vuc29yZWQpKSkKcHJpbnQocGFzdGUoIlByb3BvcnRpb24gb2YgY2Vuc29yZWQgb2JzZXJ2YXRpb25zOiIsIG1lYW4oZGF0YSRjZW5zb3JlZCkpKQoKIyBCYXNpYyB2aXN1YWxpemF0aW9uIC0gY29tcGFyZSBiYXNlbGluZSBieSBjZW5zb3Jpbmcgc3RhdHVzCmdncGxvdChkYXRhLCBhZXMoeCA9IGZhY3RvcihjZW5zb3JlZCksIHkgPSBiYXNlbGluZSwgZmlsbCA9IGdyb3VwKSkgKwogIGdlb21fYm94cGxvdCgpICsKICBnZ3RpdGxlKCJCYXNlbGluZSBTY29yZXMgYnkgR3JvdXAgYW5kIENlbnNvcmluZyBTdGF0dXMiKSArCiAgeGxhYigiQ2Vuc29yZWQgKDEgPSB5ZXMsIDAgPSBubykiKSArCiAgeWxhYigiQmFzZWxpbmUgU2NvcmUiKQoKIyBDcmVhdGUgY2hhbmdlIHNjb3JlcwpkYXRhJGNoYW5nZTEgPC0gZGF0YSR0aW1lMSAtIGRhdGEkYmFzZWxpbmUKZGF0YSRjaGFuZ2UyIDwtIGRhdGEkdGltZTIgLSBkYXRhJGJhc2VsaW5lCgojIExvbmdpdHVkaW5hbCBwbG90IHdpdGggY2Vuc29yaW5nIGluZGljYXRlZApkYXRhX2xvbmcgPC0gZGF0YSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IGMoYmFzZWxpbmUsIHRpbWUxLCB0aW1lMiksCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gInRpbWUiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAic2NvcmUiKSAlPiUKICBtdXRhdGUodGltZSA9IGZhY3Rvcih0aW1lLCBsZXZlbHMgPSBjKCJiYXNlbGluZSIsICJ0aW1lMSIsICJ0aW1lMiIpLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiQmFzZWxpbmUiLCAiVGltZSAxIiwgIlRpbWUgMiIpKSkKCiMgQWRkIHRyYW5zcGFyZW5jeSBiYXNlZCBvbiBjZW5zb3JpbmcgKGNlbnNvcmVkIGRhdGEgcG9pbnRzIGFyZSBtb3JlIHRyYW5zcGFyZW50KQpnZ3Bsb3QoZGF0YV9sb25nLCBhZXMoeCA9IHRpbWUsIHkgPSBzY29yZSwgZ3JvdXAgPSBpbnRlcmFjdGlvbihpZCwgZ3JvdXApLCAKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZ3JvdXAsIGFscGhhID0gZmFjdG9yKGNlbnNvcmVkKSkpICsKICBnZW9tX2xpbmUoKSArCiAgc2NhbGVfYWxwaGFfbWFudWFsKHZhbHVlcyA9IGMoIjAiID0gMSwgIjEiID0gMC4yKSwgbmFtZSA9ICJDZW5zb3JlZCIpICsKICBzdGF0X3N1bW1hcnkoZGF0YSA9IHN1YnNldChkYXRhX2xvbmcsICEodGltZSA9PSAiVGltZSAyIiAmIGlzLm5hKHNjb3JlKSkpLAogICAgICAgICAgICAgICBhZXMoZ3JvdXAgPSBncm91cCksIGZ1biA9IG1lYW4sIGdlb20gPSAibGluZSIsIHNpemUgPSAxLjUpICsKICBzdGF0X3N1bW1hcnkoZGF0YSA9IHN1YnNldChkYXRhX2xvbmcsICEodGltZSA9PSAiVGltZSAyIiAmIGlzLm5hKHNjb3JlKSkpLAogICAgICAgICAgICAgICBhZXMoZ3JvdXAgPSBncm91cCksIGZ1biA9IG1lYW4sIGdlb20gPSAicG9pbnQiLCBzaXplID0gMykgKwogIHRoZW1lX21pbmltYWwoKSArCiAgZ2d0aXRsZSgiQ2hhbmdlIGluIFNjb3JlcyBPdmVyIFRpbWUgYnkgR3JvdXAgKHdpdGggQ2Vuc29yaW5nKSIpICsKICB5bGFiKCJTY29yZSIpICsKICB4bGFiKCJNZWFzdXJlbWVudCBUaW1lIikKCiMgRGVmaW5lIFNFTSBtb2RlbCBmb3IgdW5jZW5zb3JlZCBkYXRhCiMgV2UgbmVlZCBhIHNlcGFyYXRlIG1vZGVsIGJlY2F1c2UgdGhlIGNlbnNvcmluZyB2YXJpYWJsZSBoYXMgbm8gdmFyaWFuY2UgaW4gdGhlIHVuY2Vuc29yZWQgc3Vic2V0Cm1vZGVsX3VuY2VucyA8LSAnCiAgIyBMYXRlbnQgdmFyaWFibGUgZm9yIHRydWUgY2hhbmdlCiAgY2hhbmdlID1+IGNoYW5nZTEgKyBjaGFuZ2UyCiAgCiAgIyBSZWdyZXNzaW9uIHBhdGhzOiBncm91cCwgYmFzZWxpbmUsIGFuZCBzZXZlcml0eSBwcmVkaWN0IGNoYW5nZQogICMgR3JvdXAgcmVwcmVzZW50cyB0cmVhdG1lbnQgZWZmZWN0CiAgIyBCYXNlbGluZSByZXByZXNlbnRzIHRoZSBwcm9wb3J0aW9uYWwgbmF0dXJlIG9mIHRoZSBlZmZlY3QKICAjIFNldmVyaXR5IHJlcHJlc2VudHMgdGhlIGhpZGRlbiBjb25mb3VuZGluZyB2YXJpYWJsZQogIGNoYW5nZSB+IGdyb3VwICsgYmFzZWxpbmUgKyBzZXZlcml0eQogIAogICMgQWxsb3cgcmVzaWR1YWwgY29ycmVsYXRpb24gYmV0d2VlbiB0aW1lIHBvaW50cwogIGNoYW5nZTEgfn4gY2hhbmdlMgonCgojIEZpdCB0aGUgbW9kZWwgdXNpbmcgb25seSB1bmNlbnNvcmVkIGRhdGEgKGNyZWF0ZXMgYmlhcykKZml0X3VuY2VucyA8LSBzZW0obW9kZWxfdW5jZW5zLCBkYXRhID0gZGF0YV91bmNlbnNvcmVkKQpzdW1tYXJ5KGZpdF91bmNlbnMsIGZpdC5tZWFzdXJlcyA9IFRSVUUsIHN0YW5kYXJkaXplZCA9IFRSVUUpCgojIE1vZGVsIHRoYXQgaWdub3JlcyBzZXZlcml0eSAobWlzc3BlY2lmaWVkKQptb2RlbF9taXNzcGVjIDwtICcKICAjIExhdGVudCB2YXJpYWJsZSBmb3IgdHJ1ZSBjaGFuZ2UKICBjaGFuZ2UgPX4gY2hhbmdlMSArIGNoYW5nZTIKICAKICAjIFJlZ3Jlc3Npb24gcGF0aHM6IG9ubHkgZ3JvdXAgYW5kIGJhc2VsaW5lIHByZWRpY3QgY2hhbmdlCiAgIyBNaXNzaW5nIHRoZSBzZXZlcml0eSB2YXJpYWJsZSAoY3JlYXRlcyBvbWl0dGVkIHZhcmlhYmxlIGJpYXMpCiAgY2hhbmdlIH4gZ3JvdXAgKyBiYXNlbGluZQogIAogICMgQWxsb3cgcmVzaWR1YWwgY29ycmVsYXRpb24gYmV0d2VlbiB0aW1lIHBvaW50cwogIGNoYW5nZTEgfn4gY2hhbmdlMgonCgojIEZpdCB0aGUgbWlzc3BlY2lmaWVkIG1vZGVsIHdpdGggYWxsIGRhdGEgdXNpbmcgRklNTApmaXRfbWlzc3BlYyA8LSBzZW0obW9kZWxfbWlzc3BlYywgZGF0YSA9IGRhdGEsIG1pc3NpbmcgPSAiZmltbCIpCnN1bW1hcnkoZml0X21pc3NwZWMsIGZpdC5tZWFzdXJlcyA9IFRSVUUsIHN0YW5kYXJkaXplZCA9IFRSVUUpCgojIEZpdCB0aGUgbWlzc3BlY2lmaWVkIG1vZGVsIHdpdGggdW5jZW5zb3JlZCBkYXRhIG9ubHkgKGRvdWJsZSBiaWFzKQpmaXRfbWlzc3BlY191bmNlbnMgPC0gc2VtKG1vZGVsX21pc3NwZWMsIGRhdGEgPSBkYXRhX3VuY2Vuc29yZWQpCnN1bW1hcnkoZml0X21pc3NwZWNfdW5jZW5zLCBmaXQubWVhc3VyZXMgPSBUUlVFLCBzdGFuZGFyZGl6ZWQgPSBUUlVFKQoKIyBDb21wYXJlIHRyZWF0bWVudCBlZmZlY3QgZXN0aW1hdGVzIGFjcm9zcyBtb2RlbHMgCiMgV2UgbmVlZCB0byBoYW5kbGUgdGhlIGNhc2Ugd2hlcmUgc29tZSBtb2RlbHMgbWF5IG5vdCBoYXZlIGJlZW4gc3VjY2Vzc2Z1bGx5IGZpdAp0cmVhdG1lbnRFZmZlY3QgPC0gZGF0YS5mcmFtZSgKICBNb2RlbCA9IGNoYXJhY3RlcigpLAogIEVzdGltYXRlID0gbnVtZXJpYygpLAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQopCgojIEV4dHJhY3QgdHJlYXRtZW50IGVmZmVjdCBmcm9tIG1vZGVsMSAKaWYoZXhpc3RzKCJmaXRfY2VucyIpKSB7CiAgaWR4IDwtIHdoaWNoKHBhcmFtZXRlckVzdGltYXRlcyhmaXRfY2VucykkbGhzID09ICJjaGFuZ2UiICYgCiAgICAgICAgICAgICAgIHBhcmFtZXRlckVzdGltYXRlcyhmaXRfY2VucykkcmhzID09ICJncm91cCIpCiAgaWYobGVuZ3RoKGlkeCkgPiAwKSB7CiAgICB0cmVhdG1lbnRFZmZlY3QgPC0gcmJpbmQodHJlYXRtZW50RWZmZWN0LCAKICAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhLmZyYW1lKE1vZGVsID0gIkNvbXBsZXRlICsgU2V2ZXJpdHkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVzdGltYXRlID0gcGFyYW1ldGVyRXN0aW1hdGVzKGZpdF9jZW5zKVtpZHgsICJlc3QiXSkpCiAgfQp9CgojIEV4dHJhY3QgZnJvbSBtb2RlbCB3aXRoIHVuY2Vuc29yZWQgZGF0YSArIHNldmVyaXR5CmlmKGV4aXN0cygiZml0X3VuY2VucyIpKSB7CiAgaWR4IDwtIHdoaWNoKHBhcmFtZXRlckVzdGltYXRlcyhmaXRfdW5jZW5zKSRsaHMgPT0gImNoYW5nZSIgJiAKICAgICAgICAgICAgICAgcGFyYW1ldGVyRXN0aW1hdGVzKGZpdF91bmNlbnMpJHJocyA9PSAiZ3JvdXAiKQogIGlmKGxlbmd0aChpZHgpID4gMCkgewogICAgdHJlYXRtZW50RWZmZWN0IDwtIHJiaW5kKHRyZWF0bWVudEVmZmVjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShNb2RlbCA9ICJVbmNlbnNvcmVkICsgU2V2ZXJpdHkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEVzdGltYXRlID0gcGFyYW1ldGVyRXN0aW1hdGVzKGZpdF91bmNlbnMpW2lkeCwgImVzdCJdKSkKICB9Cn0KCiMgRXh0cmFjdCBmcm9tIG1pc3NwZWNpZmllZCBtb2RlbAppZihleGlzdHMoImZpdF9taXNzcGVjIikpIHsKICBpZHggPC0gd2hpY2gocGFyYW1ldGVyRXN0aW1hdGVzKGZpdF9taXNzcGVjKSRsaHMgPT0gImNoYW5nZSIgJiAKICAgICAgICAgICAgICAgcGFyYW1ldGVyRXN0aW1hdGVzKGZpdF9taXNzcGVjKSRyaHMgPT0gImdyb3VwIikKICBpZihsZW5ndGgoaWR4KSA+IDApIHsKICAgIHRyZWF0bWVudEVmZmVjdCA8LSByYmluZCh0cmVhdG1lbnRFZmZlY3QsIAogICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEuZnJhbWUoTW9kZWwgPSAiQ29tcGxldGUgdy9vIFNldmVyaXR5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFc3RpbWF0ZSA9IHBhcmFtZXRlckVzdGltYXRlcyhmaXRfbWlzc3BlYylbaWR4LCAiZXN0Il0pKQogIH0KfQoKIyBFeHRyYWN0IGZyb20gbWlzc3BlY2lmaWVkIG1vZGVsIHdpdGggdW5jZW5zb3JlZCBkYXRhCmlmKGV4aXN0cygiZml0X21pc3NwZWNfdW5jZW5zIikpIHsKICBpZHggPC0gd2hpY2gocGFyYW1ldGVyRXN0aW1hdGVzKGZpdF9taXNzcGVjX3VuY2VucykkbGhzID09ICJjaGFuZ2UiICYgCiAgICAgICAgICAgICAgIHBhcmFtZXRlckVzdGltYXRlcyhmaXRfbWlzc3BlY191bmNlbnMpJHJocyA9PSAiZ3JvdXAiKQogIGlmKGxlbmd0aChpZHgpID4gMCkgewogICAgdHJlYXRtZW50RWZmZWN0IDwtIHJiaW5kKHRyZWF0bWVudEVmZmVjdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGF0YS5mcmFtZShNb2RlbCA9ICJVbmNlbnNvcmVkIHcvbyBTZXZlcml0eSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXN0aW1hdGUgPSBwYXJhbWV0ZXJFc3RpbWF0ZXMoZml0X21pc3NwZWNfdW5jZW5zKVtpZHgsICJlc3QiXSkpCiAgfQp9CgojIFByaW50IHRoZSBjb21wYXJpc29uIHRhYmxlCnByaW50KCJUcmVhdG1lbnQgRWZmZWN0IEVzdGltYXRlcyBBY3Jvc3MgTW9kZWxzOiIpCnByaW50KHRyZWF0bWVudEVmZmVjdCkKCiMgVGhpcyBjb21wYXJpc29uIHRhYmxlIHNob3dzIHRoZSBiaWFzIGludHJvZHVjZWQgYnkgYW5hbHl6aW5nIG9ubHkgdW5jZW5zb3JlZCBwYXJ0aWNpcGFudHMKIyBhbmQgYnkgb21pdHRpbmcgaW1wb3J0YW50IGNvbmZvdW5kaW5nIHZhcmlhYmxlcyBmcm9tIHRoZSBtb2RlbAoKIyBBbHRlcm5hdGl2ZSBtb2RlbCB3aXRoIGxhdGVudCB2YXJpYWJsZXMgZm9yIGVhY2ggdGltZSBwb2ludAojIEZvciBwcm9wb3J0aW9uYWwgZWZmZWN0cywgd2UgbmVlZCBpbnRlcmFjdGlvbiB0ZXJtcwptb2RlbDIgPC0gJwogICMgTGF0ZW50IHZhcmlhYmxlcyBmb3IgZWFjaCB0aW1lIHBvaW50CiAgYmFzZWxpbmUgPX4gMSpiYXNlbGluZQogIGZvbGxvd3VwMSA9fiAxKnRpbWUxCiAgZm9sbG93dXAyID1+IDEqdGltZTIKICAKICAjIFJlZ3Jlc3Npb24gcGF0aHMgZnJvbSBiYXNlbGluZSB0byBmb2xsb3ctdXBzCiAgZm9sbG93dXAxIH4gYmFzZWxpbmUKICBmb2xsb3d1cDIgfiBiYXNlbGluZSArIGZvbGxvd3VwMQogIAogICMgRWZmZWN0IG9mIHRyZWF0bWVudCBvbiBmb2xsb3ctdXBzCiAgIyBEaXJlY3QgZWZmZWN0cyBvZiBncm91cAogIGZvbGxvd3VwMSB+IGdyb3VwCiAgZm9sbG93dXAyIH4gZ3JvdXAKICAKICAjIEludGVyYWN0aW9uIGVmZmVjdHMgKHByb3BvcnRpb25hbCBlZmZlY3RzKQogICMgQ3JlYXRlIGludGVyYWN0aW9uIHRlcm0gaW4gdGhlIGRhdGEgZmlyc3QKJwoKIyBBbHRlcm5hdGl2ZSBtb2RlbCB3aXRoIGxhdGVudCB2YXJpYWJsZXMgYW5kIG1pc3NpbmcgZGF0YSBoYW5kbGluZwptb2RlbDJfY2VucyA8LSAnCiAgIyBMYXRlbnQgdmFyaWFibGVzIGZvciBlYWNoIHRpbWUgcG9pbnQgLSBhdm9pZCBuYW1lIGNvbGxpc2lvbnMgd2l0aCBvYnNlcnZlZCB2YXJpYWJsZXMKICBsYXRlbnRfYmFzZWxpbmUgPX4gMSpiYXNlbGluZQogIGxhdGVudF9mb2xsb3d1cDEgPX4gMSp0aW1lMQogIGxhdGVudF9mb2xsb3d1cDIgPX4gMSp0aW1lMgogIAogICMgUmVncmVzc2lvbiBwYXRocyBmcm9tIGJhc2VsaW5lIHRvIGZvbGxvdy11cHMKICBsYXRlbnRfZm9sbG93dXAxIH4gbGF0ZW50X2Jhc2VsaW5lCiAgbGF0ZW50X2ZvbGxvd3VwMiB+IGxhdGVudF9iYXNlbGluZSArIGxhdGVudF9mb2xsb3d1cDEKICAKICAjIEVmZmVjdCBvZiB0cmVhdG1lbnQgb24gZm9sbG93LXVwcwogICMgRGlyZWN0IGVmZmVjdHMgb2YgZ3JvdXAKICBsYXRlbnRfZm9sbG93dXAxIH4gZ3JvdXAKICBsYXRlbnRfZm9sbG93dXAyIH4gZ3JvdXAKICAKICAjIFNldmVyaXR5IGFmZmVjdHMgb3V0Y29tZXMKICBsYXRlbnRfYmFzZWxpbmUgfiBzZXZlcml0eQogIGxhdGVudF9mb2xsb3d1cDEgfiBzZXZlcml0eQogIGxhdGVudF9mb2xsb3d1cDIgfiBzZXZlcml0eQogIAogICMgQ2Vuc29yaW5nIGlzIHJlbGF0ZWQgdG8gc2V2ZXJpdHkgYW5kIHRyZWF0bWVudAogIGNlbnNvcmVkIH4gc2V2ZXJpdHkgKyBncm91cAogIAogICMgSW50ZXJhY3Rpb24gZWZmZWN0cyAocHJvcG9ydGlvbmFsIGVmZmVjdHMpCiAgbGF0ZW50X2ZvbGxvd3VwMSB+IGJhc2VsaW5lX2J5X2dyb3VwCiAgbGF0ZW50X2ZvbGxvd3VwMiB+IGJhc2VsaW5lX2J5X2dyb3VwCicKCiMgQ3JlYXRlIGludGVyYWN0aW9uIHRlcm0gZm9yIHRoZSBtb2RlbCBpZiBub3QgYWxyZWFkeSB0aGVyZQppZighImJhc2VsaW5lX2J5X2dyb3VwIiAlaW4lIG5hbWVzKGRhdGEpKSB7CiAgZGF0YSRncm91cF9udW1lcmljIDwtIGFzLm51bWVyaWMoZGF0YSRncm91cCkgLSAxICAjIENvbnZlcnQgdG8gMC8xCiAgZGF0YSRiYXNlbGluZV9ieV9ncm91cCA8LSBkYXRhJGJhc2VsaW5lICogZGF0YSRncm91cF9udW1lcmljCn0KCiMgRml0IHRoZSBjZW5zb3JpbmctYXdhcmUgbW9kZWwgd2l0aCBGSU1MCmZpdDJfY2VucyA8LSBzZW0obW9kZWwyX2NlbnMsIGRhdGEgPSBkYXRhLCBtaXNzaW5nID0gImZpbWwiKQpzdW1tYXJ5KGZpdDJfY2VucywgZml0Lm1lYXN1cmVzID0gVFJVRSwgc3RhbmRhcmRpemVkID0gVFJVRSkKCiMgVmlzdWFsaXplIHRoZSBjZW5zb3JpbmctYXdhcmUgbW9kZWwKc2VtUGF0aHMoZml0Ml9jZW5zLCB3aGF0ID0gImVzdCIsIGZhZGUgPSBGQUxTRSwgcmVzaWR1YWxzID0gRkFMU0UsIAogICAgICAgICBlZGdlLmxhYmVsLmNleCA9IDAuOCwgCiAgICAgICAgIHRpdGxlID0gRkFMU0UsIAogICAgICAgICBsYXlvdXQgPSAidHJlZTIiKQoKIyBDcmVhdGUgYSBzaW1wbGlmaWVkIHZlcnNpb24gZm9yIHVuY2Vuc29yZWQgZGF0YSBvbmx5Cm1vZGVsMl9zaW1wbGUgPC0gJwogICMgTGF0ZW50IHZhcmlhYmxlcyBmb3IgZWFjaCB0aW1lIHBvaW50IC0gYXZvaWQgbmFtZSBjb2xsaXNpb25zCiAgbGF0ZW50X2Jhc2VsaW5lID1+IDEqYmFzZWxpbmUKICBsYXRlbnRfZm9sbG93dXAxID1+IDEqdGltZTEKICBsYXRlbnRfZm9sbG93dXAyID1+IDEqdGltZTIKICAKICAjIFJlZ3Jlc3Npb24gcGF0aHMgZnJvbSBiYXNlbGluZSB0byBmb2xsb3ctdXBzCiAgbGF0ZW50X2ZvbGxvd3VwMSB+IGxhdGVudF9iYXNlbGluZQogIGxhdGVudF9mb2xsb3d1cDIgfiBsYXRlbnRfYmFzZWxpbmUgKyBsYXRlbnRfZm9sbG93dXAxCiAgCiAgIyBFZmZlY3Qgb2YgdHJlYXRtZW50IG9uIGZvbGxvdy11cHMKICAjIERpcmVjdCBlZmZlY3RzIG9mIGdyb3VwCiAgbGF0ZW50X2ZvbGxvd3VwMSB+IGdyb3VwCiAgbGF0ZW50X2ZvbGxvd3VwMiB+IGdyb3VwCiAgCiAgIyBJbnRlcmFjdGlvbiBlZmZlY3RzIChwcm9wb3J0aW9uYWwgZWZmZWN0cykKICBsYXRlbnRfZm9sbG93dXAxIH4gYmFzZWxpbmVfYnlfZ3JvdXAKICBsYXRlbnRfZm9sbG93dXAyIH4gYmFzZWxpbmVfYnlfZ3JvdXAKJwoKIyBGaXQgdGhlIHNpbXBsaWZpZWQgbW9kZWwgd2l0aCB1bmNlbnNvcmVkIGRhdGEgb25seQojIENoZWNrIGZpcnN0IHRoYXQgcmVxdWlyZWQgdmFyaWFibGVzIGV4aXN0IGluIHRoZSBkYXRhc2V0CnByaW50KCJWYXJpYWJsZXMgaW4gZGF0YV91bmNlbnNvcmVkOiIpCnByaW50KG5hbWVzKGRhdGFfdW5jZW5zb3JlZCkpCgojIFZlcmlmeSB0aGF0IGJhc2VsaW5lX2J5X2dyb3VwIGV4aXN0cyBpbiB0aGUgZGF0YXNldAppZigiYmFzZWxpbmVfYnlfZ3JvdXAiICVpbiUgbmFtZXMoZGF0YV91bmNlbnNvcmVkKSkgewogIGZpdDJfdW5jZW5zIDwtIHNlbShtb2RlbDJfc2ltcGxlLCBkYXRhID0gZGF0YV91bmNlbnNvcmVkKQogIHN1bW1hcnkoZml0Ml91bmNlbnMsIGZpdC5tZWFzdXJlcyA9IFRSVUUsIHN0YW5kYXJkaXplZCA9IFRSVUUpCn0gZWxzZSB7CiAgcHJpbnQoIkVycm9yOiBiYXNlbGluZV9ieV9ncm91cCBtaXNzaW5nIGZyb20gZGF0YV91bmNlbnNvcmVkIikKICAjIENyZWF0ZSBpdCBhZ2FpbiBqdXN0IHRvIGJlIHN1cmUKICBkYXRhX3VuY2Vuc29yZWQkZ3JvdXBfbnVtZXJpYyA8LSBhcy5udW1lcmljKGRhdGFfdW5jZW5zb3JlZCRncm91cCkgLSAxCiAgZGF0YV91bmNlbnNvcmVkJGJhc2VsaW5lX2J5X2dyb3VwIDwtIGRhdGFfdW5jZW5zb3JlZCRiYXNlbGluZSAqIGRhdGFfdW5jZW5zb3JlZCRncm91cF9udW1lcmljCiAgCiAgIyBOb3cgdHJ5IGZpdHRpbmcgdGhlIG1vZGVsCiAgZml0Ml91bmNlbnMgPC0gc2VtKG1vZGVsMl9zaW1wbGUsIGRhdGEgPSBkYXRhX3VuY2Vuc29yZWQpCiAgc3VtbWFyeShmaXQyX3VuY2VucywgZml0Lm1lYXN1cmVzID0gVFJVRSwgc3RhbmRhcmRpemVkID0gVFJVRSkKfQoKIyBJbnRlcnByZXRhdGlvbgojIEZvciBkYXRhIHdpdGggY2Vuc29yaW5nIGFuZCBwcm9wb3J0aW9uYWwgdHJlYXRtZW50IGVmZmVjdHM6CiMgCiMgSW4gdGhpcyBzaW11bGF0aW9uOgojIAojIDEuIFNFTEVDVElPTiBCSUFTIE1FQ0hBTklTTToKIyAgICAtIFdlIGNyZWF0ZWQgYSAic2V2ZXJpdHkiIGxhdGVudCB2YXJpYWJsZSB0aGF0IGFmZmVjdHM6CiMgICAgICBhKSBCYXNlbGluZSBzY29yZXMgKHNpY2tlciBwYXRpZW50cyBoYXZlIGxvd2VyIHNjb3JlcykKIyAgICAgIGIpIFRyZWF0bWVudCBlZmZlY3QgKHRyZWF0bWVudCB3b3JrcyBiZXR0ZXIgZm9yIHNpY2tlciBwYXRpZW50cykKIyAgICAgIGMpIENlbnNvcmluZyBwcm9iYWJpbGl0eSAoc2lja2VyIHBhdGllbnRzIG1vcmUgbGlrZWx5IHRvIGRyb3Agb3V0KQojICAgIC0gVHJlYXRtZW50IGdyb3VwIHBhcnRpY2lwYW50cyBhcmUgbGVzcyBsaWtlbHkgdG8gYmUgY2Vuc29yZWQKIyAgICAtIFBhcnRpY2lwYW50cyB3aXRoIGxlc3MgaW1wcm92ZW1lbnQgYXJlIG1vcmUgbGlrZWx5IHRvIGJlIGNlbnNvcmVkCiMgCiMgMi4gQklBUyBJTiBUSEUgVFJFQVRNRU5UIEVGRkVDVCBFU1RJTUFURToKIyAgICAtIENvbXBsZXRlIG1vZGVsIHdpdGggc2V2ZXJpdHkgaW5jbHVkZWQgKGZpdF9jZW5zKTogTW9zdCBhY2N1cmF0ZSBlc3RpbWF0ZQojICAgIC0gVW5jZW5zb3JlZCBkYXRhIHdpdGggc2V2ZXJpdHkgaW5jbHVkZWQgKGZpdF91bmNlbnMpOiBCaWFzZWQgZXN0aW1hdGUKIyAgICAgICh0eXBpY2FsbHkgdW5kZXJlc3RpbWF0ZXMgdHJlYXRtZW50IGVmZmVjdCBiZWNhdXNlIHNpY2tlciBwYXRpZW50cwojICAgICAgd2hvIHdvdWxkIGJlbmVmaXQgbW9yZSBhcmUgbW9yZSBsaWtlbHkgdG8gZHJvcCBvdXQpCiMgICAgLSBNb2RlbHMgd2l0aG91dCBzZXZlcml0eSAoZml0X21pc3NwZWMgJiBmaXRfbWlzc3BlY191bmNlbnMpOiBCaWFzZWQgZHVlIHRvCiMgICAgICBvbWl0dGVkIHZhcmlhYmxlIGJpYXMgKHNldmVyaXR5IGFmZmVjdHMgYm90aCBjZW5zb3JpbmcgYW5kIG91dGNvbWVzKQojIAojIDMuIFRIRSBCSUFTRVMgSU5URVJBQ1Q6CiMgICAgLSBTZWxlY3Rpb24gYmlhczogQW5hbHl6aW5nIG9ubHkgdW5jZW5zb3JlZCBwYXJ0aWNpcGFudHMKIyAgICAtIE9taXR0ZWQgdmFyaWFibGUgYmlhczogTm90IGluY2x1ZGluZyBzZXZlcml0eSB2YXJpYWJsZQojICAgIC0gVGhlIGNvbWJpbmVkIGVmZmVjdCB0eXBpY2FsbHkgbGVhZHMgdG8gdW5kZXJlc3RpbWF0aW5nIHRyZWF0bWVudCBlZmZpY2FjeQojIAojIDQuIFBST1BFUiBIQU5ETElORyBBUFBST0FDSEVTOgojICAgIC0gRnVsbCBJbmZvcm1hdGlvbiBNYXhpbXVtIExpa2VsaWhvb2QgKEZJTUwpIGZvciBtaXNzaW5nIGRhdGEKIyAgICAtIEluY2x1ZGluZyBhbGwgcmVsZXZhbnQgY29uZm91bmRlcnMgaW4gdGhlIG1vZGVsCiMgICAgLSBTZW5zaXRpdml0eSBhbmFseXNlcyBmb3IgZGlmZmVyZW50IG1pc3NpbmcgZGF0YSBtZWNoYW5pc21zCiMgICAgLSBJbnZlcnNlIHByb2JhYmlsaXR5IHdlaWdodGluZyB0byBhY2NvdW50IGZvciBzZWxlY3Rpb24gYmlhcwojIAojIFRoZSBjb21wYXJpc29uIG9mIHRyZWF0bWVudCBlZmZlY3QgZXN0aW1hdGVzIGFjcm9zcyBtb2RlbHMgc2hvd3MgaG93IG11Y2gKIyB0aGUgZXN0aW1hdGVkIGVmZmVjdCBjaGFuZ2VzIHdoZW4gdXNpbmcgZGlmZmVyZW50IGFwcHJvYWNoZXMsIGRlbW9uc3RyYXRpbmcKIyB0aGUgbWFnbml0dWRlIG9mIGJpYXMgZnJvbSBjZW5zb3JpbmcgYW5kIG1vZGVsIG1pc3NwZWNpZmljYXRpb24uCmBgYAoKYGBge3J9CiMgR0xNIEFuYWx5c2lzIGZvciBUcmVhdG1lbnQgRWZmZWN0IHdpdGggQ2Vuc29yaW5nCgojIEFzc3VtaW5nIHRoZSBkYXRhIGhhcyBhbHJlYWR5IGJlZW4gZ2VuZXJhdGVkIGFzIGluIHRoZSBwcmV2aW91cyBjb2RlCiMgYW5kIHZhcmlhYmxlcyBhcmUgaW4gdGhlICdkYXRhJyBkYXRhZnJhbWUKCiMgMS4gTmFpdmUgR0xNIGFuYWx5c2lzIGlnbm9yaW5nIGNlbnNvcmluZwojIFRoaXMgaWdub3JlcyB0aGUgbWlzc2luZyBkYXRhIHByb2JsZW0gYW5kIGp1c3QgYW5hbHl6ZXMgd2hhdCBpcyBvYnNlcnZlZAoKIyBNb2RlbCBmb3IgdGltZTIgb3V0Y29tZSwgY29tcGxldGUgY2FzZXMgb25seQptb2RlbF9uYWl2ZSA8LSBnbG0odGltZTIgfiBncm91cCArIGJhc2VsaW5lLCAKICAgICAgICAgICAgICAgICAgZGF0YSA9IHN1YnNldChkYXRhLCAhaXMubmEodGltZTIpKSwKICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gZ2F1c3NpYW4oKSkKCiMgU3VtbWFyeSBvZiBuYWl2ZSBtb2RlbApzdW1tYXJ5KG1vZGVsX25haXZlKQoKIyAyLiBNb2RlbCBmb3IgY2hhbmdlIGZyb20gYmFzZWxpbmUKIyBDYWxjdWxhdGUgYWJzb2x1dGUgY2hhbmdlIGFuZCB1c2UgYXMgb3V0Y29tZQpkYXRhJGFic19jaGFuZ2UgPC0gZGF0YSR0aW1lMiAtIGRhdGEkYmFzZWxpbmUKCm1vZGVsX2NoYW5nZSA8LSBnbG0oYWJzX2NoYW5nZSB+IGdyb3VwICsgYmFzZWxpbmUsIAogICAgICAgICAgICAgICAgICAgZGF0YSA9IHN1YnNldChkYXRhLCAhaXMubmEodGltZTIpKSwKICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGdhdXNzaWFuKCkpCgpzdW1tYXJ5KG1vZGVsX2NoYW5nZSkKCiMgMy4gTW9kZWwgZm9yIHByb3BvcnRpb25hbCBjaGFuZ2UKIyBDYWxjdWxhdGUgcHJvcG9ydGlvbmFsIGNoYW5nZSBhbmQgdXNlIGFzIG91dGNvbWUKZGF0YSRwcm9wX2NoYW5nZSA8LSAoZGF0YSR0aW1lMiAtIGRhdGEkYmFzZWxpbmUpIC8gZGF0YSRiYXNlbGluZQoKbW9kZWxfcHJvcF9jaGFuZ2UgPC0gZ2xtKHByb3BfY2hhbmdlIH4gZ3JvdXAgKyBiYXNlbGluZSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzdWJzZXQoZGF0YSwgIWlzLm5hKHRpbWUyKSksCiAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGdhdXNzaWFuKCkpCgpzdW1tYXJ5KG1vZGVsX3Byb3BfY2hhbmdlKQoKIyA0LiBNb2RlbCB0aGF0IGluY2x1ZGVzIHNldmVyaXR5ICh0aGUgaGlkZGVuIGNvbmZvdW5kZXIpCm1vZGVsX3dpdGhfc2V2ZXJpdHkgPC0gZ2xtKHRpbWUyIH4gZ3JvdXAgKyBiYXNlbGluZSArIHNldmVyaXR5LCAKICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzdWJzZXQoZGF0YSwgIWlzLm5hKHRpbWUyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBnYXVzc2lhbigpKQoKc3VtbWFyeShtb2RlbF93aXRoX3NldmVyaXR5KQoKIyA1LiBNb2RlbCBmb3IgY2Vuc29yaW5nIHByb2Nlc3MgLSBtb2RlbGluZyBmYWN0b3JzIGFmZmVjdGluZyBkcm9wb3V0Cm1vZGVsX2NlbnNvcmluZyA8LSBnbG0oY2Vuc29yZWQgfiBncm91cCArIGJhc2VsaW5lICsgc2V2ZXJpdHksIAogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSBiaW5vbWlhbChsaW5rID0gImxvZ2l0IikpCgpzdW1tYXJ5KG1vZGVsX2NlbnNvcmluZykKCiMgNi4gSW52ZXJzZSBwcm9iYWJpbGl0eSB3ZWlnaHRpbmcgdG8gYWNjb3VudCBmb3IgY2Vuc29yaW5nCiMgRmlyc3QsIHByZWRpY3QgcHJvYmFiaWxpdHkgb2YgYmVpbmcgb2JzZXJ2ZWQgKG5vdCBjZW5zb3JlZCkKZGF0YSRwX29ic2VydmVkIDwtIDEgLSBwcmVkaWN0KG1vZGVsX2NlbnNvcmluZywgdHlwZSA9ICJyZXNwb25zZSIpCgojIENyZWF0ZSB3ZWlnaHRzIGFzIGludmVyc2Ugb2YgcHJvYmFiaWxpdHkgb2YgYmVpbmcgb2JzZXJ2ZWQKZGF0YSRpcHcgPC0gMSAvIGRhdGEkcF9vYnNlcnZlZAoKIyBXZWlnaHRlZCBhbmFseXNpcwptb2RlbF9pcHcgPC0gZ2xtKHRpbWUyIH4gZ3JvdXAgKyBiYXNlbGluZSwgCiAgICAgICAgICAgICAgICBkYXRhID0gc3Vic2V0KGRhdGEsICFpcy5uYSh0aW1lMikpLAogICAgICAgICAgICAgICAgZmFtaWx5ID0gZ2F1c3NpYW4oKSwKICAgICAgICAgICAgICAgIHdlaWdodHMgPSBpcHcpCgpzdW1tYXJ5KG1vZGVsX2lwdykKCiMgNy4gTW9kZWwgd2l0aCBpbnRlcmFjdGlvbiBiZXR3ZWVuIGJhc2VsaW5lIGFuZCB0cmVhdG1lbnQKbW9kZWxfaW50ZXJhY3Rpb24gPC0gZ2xtKHRpbWUyIH4gZ3JvdXAgKiBiYXNlbGluZSArIHNldmVyaXR5LCAKICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gc3Vic2V0KGRhdGEsICFpcy5uYSh0aW1lMikpLAogICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGdhdXNzaWFuKCkpCgpzdW1tYXJ5KG1vZGVsX2ludGVyYWN0aW9uKQoKIyA4LiBDb21wYXJlIHRyZWF0bWVudCBlZmZlY3QgZXN0aW1hdGVzIGFjcm9zcyBkaWZmZXJlbnQgbW9kZWxzCiMgRXh0cmFjdCBjb2VmZmljaWVudHMgZm9yIHRoZSB0cmVhdG1lbnQgZWZmZWN0IChncm91cCkKdHJlYXRtZW50X2VmZmVjdHMgPC0gZGF0YS5mcmFtZSgKICBNb2RlbCA9IGMoCiAgICAiTmFpdmUgKENvbXBsZXRlIENhc2VzKSIsCiAgICAiQ2hhbmdlIFNjb3JlIiwKICAgICJQcm9wb3J0aW9uYWwgQ2hhbmdlIiwKICAgICJXaXRoIFNldmVyaXR5IiwKICAgICJJUFcgZm9yIENlbnNvcmluZyIsCiAgICAiV2l0aCBJbnRlcmFjdGlvbiIKICApLAogIEVzdGltYXRlID0gYygKICAgIGNvZWYobW9kZWxfbmFpdmUpWyJncm91cFRyZWF0bWVudCJdLAogICAgY29lZihtb2RlbF9jaGFuZ2UpWyJncm91cFRyZWF0bWVudCJdLAogICAgY29lZihtb2RlbF9wcm9wX2NoYW5nZSlbImdyb3VwVHJlYXRtZW50Il0sCiAgICBjb2VmKG1vZGVsX3dpdGhfc2V2ZXJpdHkpWyJncm91cFRyZWF0bWVudCJdLAogICAgY29lZihtb2RlbF9pcHcpWyJncm91cFRyZWF0bWVudCJdLAogICAgY29lZihtb2RlbF9pbnRlcmFjdGlvbilbImdyb3VwVHJlYXRtZW50Il0KICApLAogIFNFID0gYygKICAgIHN1bW1hcnkobW9kZWxfbmFpdmUpJGNvZWZmaWNpZW50c1siZ3JvdXBUcmVhdG1lbnQiLCAiU3RkLiBFcnJvciJdLAogICAgc3VtbWFyeShtb2RlbF9jaGFuZ2UpJGNvZWZmaWNpZW50c1siZ3JvdXBUcmVhdG1lbnQiLCAiU3RkLiBFcnJvciJdLAogICAgc3VtbWFyeShtb2RlbF9wcm9wX2NoYW5nZSkkY29lZmZpY2llbnRzWyJncm91cFRyZWF0bWVudCIsICJTdGQuIEVycm9yIl0sCiAgICBzdW1tYXJ5KG1vZGVsX3dpdGhfc2V2ZXJpdHkpJGNvZWZmaWNpZW50c1siZ3JvdXBUcmVhdG1lbnQiLCAiU3RkLiBFcnJvciJdLAogICAgc3VtbWFyeShtb2RlbF9pcHcpJGNvZWZmaWNpZW50c1siZ3JvdXBUcmVhdG1lbnQiLCAiU3RkLiBFcnJvciJdLAogICAgc3VtbWFyeShtb2RlbF9pbnRlcmFjdGlvbikkY29lZmZpY2llbnRzWyJncm91cFRyZWF0bWVudCIsICJTdGQuIEVycm9yIl0KICApCikKCnRyZWF0bWVudF9lZmZlY3RzJExvd2VyX0NJIDwtIHRyZWF0bWVudF9lZmZlY3RzJEVzdGltYXRlIC0gMS45NiAqIHRyZWF0bWVudF9lZmZlY3RzJFNFCnRyZWF0bWVudF9lZmZlY3RzJFVwcGVyX0NJIDwtIHRyZWF0bWVudF9lZmZlY3RzJEVzdGltYXRlICsgMS45NiAqIHRyZWF0bWVudF9lZmZlY3RzJFNFCgojIFByaW50IGNvbXBhcmlzb24gdGFibGUKcHJpbnQodHJlYXRtZW50X2VmZmVjdHMpCgojIDkuIFZpc3VhbGl6YXRpb24gb2YgdHJlYXRtZW50IGVmZmVjdHMgYWNyb3NzIG1vZGVscwpsaWJyYXJ5KGdncGxvdDIpCgpnZ3Bsb3QodHJlYXRtZW50X2VmZmVjdHMsIGFlcyh4ID0gTW9kZWwsIHkgPSBFc3RpbWF0ZSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgZ2VvbV9lcnJvcmJhcihhZXMoeW1pbiA9IExvd2VyX0NJLCB5bWF4ID0gVXBwZXJfQ0kpLCB3aWR0aCA9IDAuMikgKwogIGNvb3JkX2ZsaXAoKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICBnZ3RpdGxlKCJUcmVhdG1lbnQgRWZmZWN0IEVzdGltYXRlcyBBY3Jvc3MgRGlmZmVyZW50IEdMTSBNb2RlbHMiKSArCiAgeWxhYigiRXN0aW1hdGVkIFRyZWF0bWVudCBFZmZlY3QiKSArCiAgeGxhYigiIikKCiMgMTAuIENhbGN1bGF0ZSBtYXJnaW5hbCBlZmZlY3RzIG9mIHRyZWF0bWVudCBhdCBkaWZmZXJlbnQgYmFzZWxpbmUgdmFsdWVzCiMgVGhpcyBzaG93cyBob3cgdGhlIHRyZWF0bWVudCBlZmZlY3QgdmFyaWVzIHdpdGggYmFzZWxpbmUKIyBpZihyZXF1aXJlTmFtZXNwYWNlKCJtYXJnaW5zIiwgcXVpZXRseSA9IFRSVUUpKSB7CiMgICBsaWJyYXJ5KG1hcmdpbnMpCiMgICAKIyAgICMgRm9yIHRoZSBpbnRlcmFjdGlvbiBtb2RlbAojICAgbWFyZ19lZmZlY3RzIDwtIG1hcmdpbnMobW9kZWxfaW50ZXJhY3Rpb24sIAojICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXMgPSAiZ3JvdXAiLAojICAgICAgICAgICAgICAgICAgICAgICAgICBhdCA9IGxpc3QoYmFzZWxpbmUgPSBzZXEobWluKGRhdGEkYmFzZWxpbmUsIG5hLnJtID0gVFJVRSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGRhdGEkYmFzZWxpbmUsIG5hLnJtID0gVFJVRSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDEwKSkpCiMgICAKIyAgIHN1bW1hcnkobWFyZ19lZmZlY3RzKQojICAgCiMgICAjIFBsb3QgdGhlIG1hcmdpbmFsIGVmZmVjdHMKIyAgIHBsb3QobWFyZ19lZmZlY3RzKQojIH0gZWxzZSB7CiAgIyBNYW51YWwgY2FsY3VsYXRpb24gb2YgbWFyZ2luYWwgZWZmZWN0cyBhdCBkaWZmZXJlbnQgYmFzZWxpbmUgdmFsdWVzCiAgYmFzZWxpbmVfdmFsdWVzIDwtIHNlcShtaW4oZGF0YSRiYXNlbGluZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGRhdGEkYmFzZWxpbmUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSAxMCkKICAKICAjIEV4dHJhY3QgY29lZmZpY2llbnRzCiAgYl9ncm91cCA8LSBjb2VmKG1vZGVsX2ludGVyYWN0aW9uKVsiZ3JvdXBUcmVhdG1lbnQiXQogIGJfaW50ZXJhY3Rpb24gPC0gY29lZihtb2RlbF9pbnRlcmFjdGlvbilbImdyb3VwVHJlYXRtZW50OmJhc2VsaW5lIl0KICAKICAjIENhbGN1bGF0ZSBtYXJnaW5hbCBlZmZlY3QgYXQgZWFjaCBiYXNlbGluZSB2YWx1ZQogIG1hcmdpbmFsX2VmZmVjdHMgPC0gZGF0YS5mcmFtZSgKICAgIGJhc2VsaW5lID0gYmFzZWxpbmVfdmFsdWVzLAogICAgZWZmZWN0ID0gYl9ncm91cCArIGJfaW50ZXJhY3Rpb24gKiBiYXNlbGluZV92YWx1ZXMKICApCiAgCiAgIyBQbG90IHRoZSBtYXJnaW5hbCBlZmZlY3RzCiAgZ2dwbG90KG1hcmdpbmFsX2VmZmVjdHMsIGFlcyh4ID0gYmFzZWxpbmUsIHkgPSBlZmZlY3QpKSArCiAgICBnZW9tX2xpbmUoKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGdndGl0bGUoIk1hcmdpbmFsIEVmZmVjdCBvZiBUcmVhdG1lbnQgYXQgRGlmZmVyZW50IEJhc2VsaW5lIFZhbHVlcyIpICsKICAgIHlsYWIoIlRyZWF0bWVudCBFZmZlY3QiKSArCiAgICB4bGFiKCJCYXNlbGluZSBWYWx1ZSIpCiN9CgojIDExLiBDYWxjdWxhdGUgdGhlIHRvdGFsIGVmZmVjdCBvZiB0cmVhdG1lbnQgb24gdGltZTIKIyBGb3IgbW9kZWxzIHdpdGhvdXQgaW50ZXJhY3Rpb24sIHRoaXMgaXMganVzdCB0aGUgY29lZmZpY2llbnQgb2YgZ3JvdXAKIyBGb3IgbW9kZWxzIHdpdGggaW50ZXJhY3Rpb24sIHdlIG5lZWQgdG8gZXZhbHVhdGUgYXQgdGhlIG1lYW4gYmFzZWxpbmUKCiMgTWVhbiBiYXNlbGluZSB2YWx1ZQptZWFuX2Jhc2VsaW5lIDwtIG1lYW4oZGF0YSRiYXNlbGluZSwgbmEucm0gPSBUUlVFKQoKIyBUb3RhbCBlZmZlY3QgaW4gaW50ZXJhY3Rpb24gbW9kZWwKdG90YWxfZWZmZWN0IDwtIGNvZWYobW9kZWxfaW50ZXJhY3Rpb24pWyJncm91cFRyZWF0bWVudCJdICsgCiAgICAgICAgICAgICAgIGNvZWYobW9kZWxfaW50ZXJhY3Rpb24pWyJncm91cFRyZWF0bWVudDpiYXNlbGluZSJdICogbWVhbl9iYXNlbGluZQoKY2F0KCJUb3RhbCBlZmZlY3Qgb2YgdHJlYXRtZW50IG9uIHRpbWUyIGF0IG1lYW4gYmFzZWxpbmU6IiwgdG90YWxfZWZmZWN0LCAiXG4iKQoKIyAxMi4gUHJlZGljdGVkIG91dGNvbWVzIGZvciB0cmVhdG1lbnQgdnMgY29udHJvbAojIENyZWF0ZSBwcmVkaWN0aW9uIGRhdGEgZm9yIGEgcmFuZ2Ugb2YgYmFzZWxpbmUgdmFsdWVzCnByZWRfZGF0YSA8LSBleHBhbmQuZ3JpZCgKICBncm91cCA9IGZhY3RvcihjKCJDb250cm9sIiwgIlRyZWF0bWVudCIpLCBsZXZlbHMgPSBsZXZlbHMoZGF0YSRncm91cCkpLAogIGJhc2VsaW5lID0gc2VxKG1pbihkYXRhJGJhc2VsaW5lLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgbWF4KGRhdGEkYmFzZWxpbmUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gMjApLAogIHNldmVyaXR5ID0gbWVhbihkYXRhJHNldmVyaXR5LCBuYS5ybSA9IFRSVUUpICAjIEZpeCBzZXZlcml0eSBhdCBtZWFuCikKCiMgUHJlZGljdGlvbnMgZnJvbSBkaWZmZXJlbnQgbW9kZWxzCnByZWRfZGF0YSRwcmVkX25haXZlIDwtIHByZWRpY3QobW9kZWxfbmFpdmUsIG5ld2RhdGEgPSBwcmVkX2RhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQpwcmVkX2RhdGEkcHJlZF93aXRoX3NldmVyaXR5IDwtIHByZWRpY3QobW9kZWxfd2l0aF9zZXZlcml0eSwgbmV3ZGF0YSA9IHByZWRfZGF0YSwgdHlwZSA9ICJyZXNwb25zZSIpCnByZWRfZGF0YSRwcmVkX2ludGVyYWN0aW9uIDwtIHByZWRpY3QobW9kZWxfaW50ZXJhY3Rpb24sIG5ld2RhdGEgPSBwcmVkX2RhdGEsIHR5cGUgPSAicmVzcG9uc2UiKQoKIyBQbG90IHByZWRpY3Rpb25zCmdncGxvdChwcmVkX2RhdGEsIGFlcyh4ID0gYmFzZWxpbmUsIHkgPSBwcmVkX2ludGVyYWN0aW9uLCBjb2xvciA9IGdyb3VwKSkgKwogIGdlb21fbGluZSgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIGdndGl0bGUoIlByZWRpY3RlZCBUaW1lMiBWYWx1ZXMgYnkgVHJlYXRtZW50IEdyb3VwIGFuZCBCYXNlbGluZSIpICsKICB5bGFiKCJQcmVkaWN0ZWQgVGltZTIiKSArCiAgeGxhYigiQmFzZWxpbmUgVmFsdWUiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCAicmVkIikpICsKICB0aGVtZShsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCgojIEFkZGl0aW9uYWwgYW5hbHlzaXM6IE1hcmdpbmFsIGVmZmVjdHMgZm9yIElQVyBpbnRlcmFjdGlvbiBtb2RlbAoKbW9kZWxfaW50ZXJhY3Rpb25faXB3IDwtIGdsbSh0aW1lMiB+IGdyb3VwICogYmFzZWxpbmUgKyBzZXZlcml0eSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgPSBzdWJzZXQoZGF0YSwgIWlzLm5hKHRpbWUyKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IGdhdXNzaWFuKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHdlaWdodHMgPSBpcHcpCgppZihyZXF1aXJlTmFtZXNwYWNlKCJtYXJnaW5zIiwgcXVpZXRseSA9IFRSVUUpKSB7CiAgbGlicmFyeShtYXJnaW5zKQogIAogICMgTWFyZ2luYWwgZWZmZWN0cyBmb3Igc3RhbmRhcmQgaW50ZXJhY3Rpb24gbW9kZWwKICBtYXJnX2VmZmVjdHNfc3RkIDwtIG1hcmdpbnMobW9kZWxfaW50ZXJhY3Rpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGVzID0gImdyb3VwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF0ID0gbGlzdChiYXNlbGluZSA9IHNlcShtaW4oZGF0YSRiYXNlbGluZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4KGRhdGEkYmFzZWxpbmUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlbmd0aC5vdXQgPSAxMCkpKQogIAogICMgTWFyZ2luYWwgZWZmZWN0cyBmb3IgSVBXIGludGVyYWN0aW9uIG1vZGVsCiAgbWFyZ19lZmZlY3RzX2lwdyA8LSBtYXJnaW5zKG1vZGVsX2ludGVyYWN0aW9uX2lwdywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJpYWJsZXMgPSAiZ3JvdXAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgYXQgPSBsaXN0KGJhc2VsaW5lID0gc2VxKG1pbihkYXRhJGJhc2VsaW5lLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXgoZGF0YSRiYXNlbGluZSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVuZ3RoLm91dCA9IDEwKSkpCiAgCiAgIyBDb21iaW5lIGZvciBjb21wYXJpc29uCiAgbWFyZ19jb21iaW5lZCA8LSBkYXRhLmZyYW1lKAogICAgYmFzZWxpbmUgPSBhdHRyKG1hcmdfZWZmZWN0c19zdGQsICJhdCIpJGJhc2VsaW5lLAogICAgZWZmZWN0X3N0ZCA9IHN1bW1hcnkobWFyZ19lZmZlY3RzX3N0ZCkkQU1FLAogICAgZWZmZWN0X2lwdyA9IHN1bW1hcnkobWFyZ19lZmZlY3RzX2lwdykkQU1FCiAgKQogIAogICMgUGxvdCBjb21wYXJpc29uIG9mIG1hcmdpbmFsIGVmZmVjdHMKICBnZ3Bsb3QobWFyZ19jb21iaW5lZCkgKwogICAgZ2VvbV9saW5lKGFlcyh4ID0gYmFzZWxpbmUsIHkgPSBlZmZlY3Rfc3RkLCBjb2xvciA9ICJTdGFuZGFyZCBNb2RlbCIpKSArCiAgICBnZW9tX2xpbmUoYWVzKHggPSBiYXNlbGluZSwgeSA9IGVmZmVjdF9pcHcsIGNvbG9yID0gIklQVyBNb2RlbCIpKSArCiAgICBnZW9tX3BvaW50KGFlcyh4ID0gYmFzZWxpbmUsIHkgPSBlZmZlY3Rfc3RkLCBjb2xvciA9ICJTdGFuZGFyZCBNb2RlbCIpKSArCiAgICBnZW9tX3BvaW50KGFlcyh4ID0gYmFzZWxpbmUsIHkgPSBlZmZlY3RfaXB3LCBjb2xvciA9ICJJUFcgTW9kZWwiKSkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGdndGl0bGUoIk1hcmdpbmFsIEVmZmVjdCBvZiBUcmVhdG1lbnQgYXQgRGlmZmVyZW50IEJhc2VsaW5lIFZhbHVlcyIpICsKICAgIHlsYWIoIlRyZWF0bWVudCBFZmZlY3QiKSArCiAgICB4bGFiKCJCYXNlbGluZSBWYWx1ZSIpICsKICAgIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibHVlIiwgInJlZCIpLCBuYW1lID0gIk1vZGVsIFR5cGUiKQp9IGVsc2UgewogICMgTWFudWFsIGNhbGN1bGF0aW9uIGZvciBib3RoIG1vZGVscwogIGJhc2VsaW5lX3ZhbHVlcyA8LSBzZXEobWluKGRhdGEkYmFzZWxpbmUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgIG1heChkYXRhJGJhc2VsaW5lLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZW5ndGgub3V0ID0gMTApCiAgCiAgIyBFeHRyYWN0IGNvZWZmaWNpZW50cyBmb3Igc3RhbmRhcmQgbW9kZWwKICBiX2dyb3VwX3N0ZCA8LSBjb2VmKG1vZGVsX2ludGVyYWN0aW9uKVsiZ3JvdXBUcmVhdG1lbnQiXQogIGJfaW50ZXJhY3Rpb25fc3RkIDwtIGNvZWYobW9kZWxfaW50ZXJhY3Rpb24pWyJncm91cFRyZWF0bWVudDpiYXNlbGluZSJdCiAgCiAgIyBFeHRyYWN0IGNvZWZmaWNpZW50cyBmb3IgSVBXIG1vZGVsCiAgYl9ncm91cF9pcHcgPC0gY29lZihtb2RlbF9pbnRlcmFjdGlvbl9pcHcpWyJncm91cFRyZWF0bWVudCJdCiAgYl9pbnRlcmFjdGlvbl9pcHcgPC0gY29lZihtb2RlbF9pbnRlcmFjdGlvbl9pcHcpWyJncm91cFRyZWF0bWVudDpiYXNlbGluZSJdCiAgCiAgIyBDYWxjdWxhdGUgbWFyZ2luYWwgZWZmZWN0cwogIG1hcmdpbmFsX2VmZmVjdHMgPC0gZGF0YS5mcmFtZSgKICAgIGJhc2VsaW5lID0gYmFzZWxpbmVfdmFsdWVzLAogICAgZWZmZWN0X3N0ZCA9IGJfZ3JvdXBfc3RkICsgYl9pbnRlcmFjdGlvbl9zdGQgKiBiYXNlbGluZV92YWx1ZXMsCiAgICBlZmZlY3RfaXB3ID0gYl9ncm91cF9pcHcgKyBiX2ludGVyYWN0aW9uX2lwdyAqIGJhc2VsaW5lX3ZhbHVlcwogICkKICAKICAjIFBsb3QgY29tcGFyaXNvbgogIGdncGxvdChtYXJnaW5hbF9lZmZlY3RzKSArCiAgICBnZW9tX2xpbmUoYWVzKHggPSBiYXNlbGluZSwgeSA9IGVmZmVjdF9zdGQsIGNvbG9yID0gIlN0YW5kYXJkIE1vZGVsIikpICsKICAgIGdlb21fbGluZShhZXMoeCA9IGJhc2VsaW5lLCB5ID0gZWZmZWN0X2lwdywgY29sb3IgPSAiSVBXIE1vZGVsIikpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSBiYXNlbGluZSwgeSA9IGVmZmVjdF9zdGQsIGNvbG9yID0gIlN0YW5kYXJkIE1vZGVsIikpICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSBiYXNlbGluZSwgeSA9IGVmZmVjdF9pcHcsIGNvbG9yID0gIklQVyBNb2RlbCIpKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgZ2d0aXRsZSgiTWFyZ2luYWwgRWZmZWN0IG9mIFRyZWF0bWVudCBhdCBEaWZmZXJlbnQgQmFzZWxpbmUgVmFsdWVzIikgKwogICAgeWxhYigiVHJlYXRtZW50IEVmZmVjdCIpICsKICAgIHhsYWIoIkJhc2VsaW5lIFZhbHVlIikgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoImJsdWUiLCAicmVkIiksIG5hbWUgPSAiTW9kZWwgVHlwZSIpCn0KCgpgYGAKCiMjIEFuYWx5emUgbm9uLWNlbnNvcmVkIHZlcnNpb24sIGluY2x1ZGluZyBzZXZlcml0eS4gV2hlbiBubyBwYXRpZW50cyBhcmUgY2Vuc29yZWQgc2V2ZXJpdHkgc2hvdWxkIG5vdCBiZSBuZWVkZWQuCgpgYGB7cn0KZ2xtKGZvcm11bGEgPSB0aW1lMl9hbGwgfiBncm91cCArIGJhc2VsaW5lICsgc2V2ZXJpdHksIGZhbWlseSA9IGdhdXNzaWFuKCksIAogICAgZGF0YSA9IGRhdGEpCmBgYAoKIyBSZW1vdmluZyBzZXZlcml0eSBmcm9tIHRoZSBtb2RlbAoKYGBge3J9CmdsbShmb3JtdWxhID0gdGltZTJfYWxsIH4gZ3JvdXAgKyBiYXNlbGluZSwgZmFtaWx5ID0gZ2F1c3NpYW4oKSwgCiAgICBkYXRhID0gZGF0YSkKYGBgCgojIEFkZGluZyBiYXNlbGluZSBhcyBhbiBpbnRlcmFjdGlvbiB3aXRoIHRyZWF0bWVudCBncm91cAoKYGBge3J9CmdsbShmb3JtdWxhID0gdGltZTJfYWxsIH4gZ3JvdXAqYmFzZWxpbmUsIGZhbWlseSA9IGdhdXNzaWFuKCksIAogICAgZGF0YSA9IGRhdGEpCmBgYAoKIyMgQW5hbHlzaXMgdXNpbmcgc3RhYmlsaXplZCBpbnZlcnNlIHByb2JhYmlsaXR5IHdlaWdodHMgdG8gYWRqdXN0IGZvciBjZW5zb3JpbmcuCgpUcmVhdG1lbnQgaXMgcmFuZG9taXplZCBpbiB0aGlzIGRhdGEsIHNvIG5vIG5lZWQgdG8gYWRqdXN0IGZvciB0cmVhdG1lbnQgYXNzaWdubWVudAoKYGBge3J9CiMjIyBFc3RpbWF0ZSBzdGFiaWxpemVkIGludmVyc2UgcHJvYmFiaWxpdHkgd2VpZ2h0cyBmb3IgY2Vuc29yaW5nICMjIwoKIyBGaXQgYSBwb29sZWQgbG9naXN0aWMgbW9kZWwgdGhhdCBwcmVkaWN0cyB0aGUgcHJvYmFiaWxpdHkgb2YgcmVtYWluaW5nIHVuY2Vuc29yZWQgCiMgKG5vdCBiZWluZyBsb3N0IHRvIGZvbGxvdy11cCkKcHNjLmRlbm9tIDwtIGdsbShjZW5zb3JlZCA9PSAwIH4gZ3JvdXAgKyBiYXNlbGluZSArIHNldmVyaXR5ICwgCiAgICAgICAgICAgICAgICBmYW1pbHk9Ymlub21pYWwobGluaz0ibG9naXQiKSwKICAgICAgICAgICAgICAgIGRhdGE9ZGF0YSkKc3VtbWFyeShwc2MuZGVub20pCgojIE9idGFpbiBwcmVkaWN0ZWQgcHJvYmFiaWxpdGllcyBmb3IgdGhlIGRlbm9taW5hdG9yCmRhdGEkcHNjLmRlbm9tIDwtIHByZWRpY3QocHNjLmRlbm9tLCBkYXRhLCB0eXBlPSJyZXNwb25zZSIpIAoKIyBGaXQgbW9kZWwgZm9yIHRoZSBudW1lcmF0b3Igb2YgdGhlIHN0YWJpbGl6ZWQgd2VpZ2h0cwpwc2MubnVtIDwtIGdsbShjZW5zb3JlZD09MCB+IGdyb3VwLCAKICAgICAgICAgICAgICAgZmFtaWx5PWJpbm9taWFsKGxpbms9ImxvZ2l0IiksIGRhdGE9ZGF0YSkKc3VtbWFyeShwc2MubnVtKQoKIyBFc3RpbWF0ZSBzdGFiaWxpemVkIHdlaWdodHMKZGF0YSRwc2MubnVtIDwtIHByZWRpY3QocHNjLm51bSwgZGF0YSwgdHlwZT0icmVzcG9uc2UiKQoKZGF0YSA8LSBkYXRhICU+JQogIGdyb3VwX2J5KGlkKSAlPiUKICBtdXRhdGUoCiAgICBzd19jID0gY3VtcHJvZChwc2MubnVtKS9jdW1wcm9kKHBzYy5kZW5vbSkKICApICU+JQogIHVuZ3JvdXAoKSAlPiUKICBtdXRhdGUoc3dfYyA9IGlmZWxzZShpcy5uYShjZW5zb3JlZCksIDEsIHN3X2MpKSAKCgojIyMgIE1pbiwgMjV0aCBwZXJjZW50aWxlLCBtZWRpYW4sIG1lYW4sIFNELCA3NXRoIHBlcmNlbnRpbGUsIGFuZCBtYXg6IHN0YWJpbGl6ZWQgd2VpZ2h0cyAjIyMKc3VtbWFyeShkYXRhJHN3X2MpCnNkKGRhdGEkc3dfYykKYGBgCgpOb3RlIHRoYXQgdGhlIGBlY2hvID0gRkFMU0VgIHBhcmFtZXRlciB3YXMgYWRkZWQgdG8gdGhlIGNvZGUgY2h1bmsgdG8gcHJldmVudCBwcmludGluZyBvZiB0aGUgUiBjb2RlIHRoYXQgZ2VuZXJhdGVkIHRoZSBwbG90Lgo=